各种骚操作线段树

线段树是世界上最美的数据结构(主要记录一些有意义的线段树.....特别是骚操作

1.uestc1425 Another LCIS  http://acm.uestc.edu.cn/#/problem/show/360

题意:两种操作 对于一段区间的数加上c 查询最长连续上升序列

题解:彻底弄清楚区间更新lazy的含义 就是切水题 直接区间更新然后区间合并用点小技巧即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#define N 100005
using namespace std;
typedef struct node{
    int l;int r;int l1;int len1;int r1;int len2;
    int len;int flag;
}node;
node d[N<<2];
int a[N];
void up(int root){
    d[root].l1=d[root<<1].l1;d[root].r1=d[root<<1|1].r1;
    if(d[root<<1].r1<d[root<<1|1].l1){
        d[root].len=max(d[root<<1].len,max(d[root<<1].len2+d[root<<1|1].len1,d[root<<1|1].len));
        if(d[root<<1].len1==(d[root<<1].r-d[root<<1].l+1))
             d[root].len1=d[root<<1].len1+d[root<<1|1].len1;
        else d[root].len1=d[root<<1].len1;
        if(d[root<<1|1].len2==(d[root<<1|1].r-d[root<<1|1].l+1))
             d[root].len2=d[root<<1].len2+d[root<<1|1].len2;
        else d[root].len2=d[root<<1|1].len2;
    }
    else{
        d[root].len=max(d[root<<1].len,d[root<<1|1].len);
        d[root].len1=d[root<<1].len1;d[root].len2=d[root<<1|1].len2;
    }
}
void push(int root){
    d[root<<1].l1+=d[root].flag;d[root<<1].r1+=d[root].flag;
    d[root<<1|1].l1+=d[root].flag;d[root<<1|1].r1+=d[root].flag;
    d[root<<1].flag+=d[root].flag;d[root<<1|1].flag+=d[root].flag;
    d[root].flag=0;
}
void built(int root,int l,int r){
    if(l==r){
        d[root].l=l;d[root].r=r;d[root].l1=a[l];d[root].r1=a[l];d[root].len1=1;d[root].len2=1;d[root].len=1;
        d[root].flag=0;
        return ;
    }
    int mid=(l+r)>>1;
    built(root<<1,l,mid);
    built(root<<1|1,mid+1,r);
    d[root].l=d[root<<1].l;d[root].r=d[root<<1|1].r;d[root].flag=0;up(root);
}
void update(int root,int l,int r,int e){
    if(l<=d[root].l&&d[root].r<=r){
        d[root].flag+=e;
        d[root].l1+=e;d[root].r1+=e;
        return ;
    }
    push(root);
    int mid=(d[root].l+d[root].r)>>1;
    if(l<=mid) update(root<<1,l,r,e);
    if(r>mid) update(root<<1|1,l,r,e);
    up(root);
}
int ans;int tt;node ttt;
void genxin(int root){
        ttt.len=max(ttt.len,max(ttt.len2+d[root].len1,d[root].len));
        if(ttt.len1==(ttt.r-ttt.l+1))
             ttt.len1=ttt.len1+d[root].len1;
        else ttt.len1=ttt.len1;
        if(d[root].len2==(d[root].r-d[root].l+1))
             ttt.len2=ttt.len2+d[root].len2;
        else ttt.len2=d[root].len2;
        ttt.r1=d[root].r1;
}
void querty(int root,int l,int r){
    if(l<=d[root].l&&d[root].r<=r){
        if(tt==0) {ans=d[root].len;ttt=d[root];}
        else{
          //  cout<<ttt.r1<<" "<<d[root].l1<<endl;
            if(ttt.r1<d[root].l1){
                genxin(root);
                ans=max(ans,ttt.len);
            }
            else{
                ans=max(ans,d[root].len);ttt=d[root];
            }
        }tt=1;
  //  cout<<d[root].l<<" "<<d[root].r<<" "<<ans<<endl;
        return ;
    }
    push(root);
    int mid=(d[root].l+d[root].r)>>1;
    if(l<=mid) querty(root<<1,l,r);
    if(r>mid) querty(root<<1|1,l,r);
    up(root);
}
int main(){
    int Case=0;int T;
  //  freopen("1.txt","r",stdin);
    scanf("%d",&T);
    char str[10];
    while(T--){
        int n,m;scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        built(1,1,n);
        printf("Case #%d:\n",++Case);
        for(int i=1;i<=m;i++){
            scanf("%s",str);
            int t1,t2,t3;
            if(str[0]==‘a‘){
                scanf("%d%d%d",&t1,&t2,&t3);
                update(1,t1,t2,t3);
            }
            else if(str[0]==‘q‘){
                scanf("%d%d",&t1,&t2);
                tt=0;
                querty(1,t1,t2);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

 2.POJ 2991http://poj.org/problem?id=2991

题意:有n根棍子 每根棍子直接用一个节点相连 可旋转  有m种操作 即旋转s和s+1直接的节点 使节点成a角度 对于每次变化输出最后一个点的位置

题解:由向量旋转定理 对于增加的角度b 有x1=x0*cosb-y0*sinb;y1=sinb*x0+cosb*y0;然后线段树区间维护即可

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cmath>
#define N 20005
#define pi (acos(-1.0))
using namespace std;
typedef struct node{
    int l;int r;double t1,t2;
    int t;
    int flag;
}node;
node d[N<<2];
int a[N];
void upsh(int root){
    d[root].t1=d[root<<1].t1+d[root<<1|1].t1;
    d[root].t2=d[root<<1].t2+d[root<<1|1].t2;
}
void built(int root,int l,int r){
    if(l==r){
        d[root].l=l;d[root].r=r;d[root].t1=0;d[root].t2=a[l];
        d[root].flag=0;d[root].t=0;
        return ;
    }
    int mid=(l+r)>>1;
    built(root<<1,l,mid);
    built(root<<1|1,mid+1,r);
    d[root].l=d[root<<1].l;d[root].r=d[root<<1|1].r;upsh(root);d[root].flag=0;
    d[root].t=0;
}
void uppush(int root){
    double tt=d[root].flag*1.0*pi/180;
    double ttt1,ttt2;
    d[root<<1].t=(d[root].flag+d[root<<1].t)%360;d[root<<1|1].t=(d[root].flag+d[root<<1|1].t)%360;
    d[root].t2=cos(tt)*ttt2+ttt1*sin(tt);
    ttt1=d[root<<1].t1;
    ttt2=d[root<<1].t2;
    d[root<<1].t1=ttt1*cos(tt)-ttt2*sin(tt);
    d[root<<1].t2=cos(tt)*ttt2+ttt1*sin(tt);
    ttt1=d[root<<1|1].t1;
    ttt2=d[root<<1|1].t2;
    d[root<<1|1].t1=ttt1*cos(tt)-ttt2*sin(tt);
    d[root<<1|1].t2=cos(tt)*ttt2+ttt1*sin(tt);
    d[root<<1].flag=(d[root<<1].flag+d[root].flag)%360;d[root<<1|1].flag=(d[root<<1|1].flag+d[root].flag)%360;
    d[root].flag=0;
}
void update(int root,int l,int r,int e){
    if(l<=d[root].l&&d[root].r<=r){
    d[root].flag=(d[root].flag+e)%360;d[root].t=(d[root].t+e)%360;
    double tt=e*1.0*pi/180;
    double ttt1=d[root].t1;
    double ttt2=d[root].t2;
    d[root].t1=ttt1*cos(tt)-ttt2*sin(tt);
    d[root].t2=cos(tt)*ttt2+ttt1*sin(tt);
    return ;
    }
    uppush(root);
    int mid=(d[root].l+d[root].r)>>1;
    if(l<=mid) update(root<<1,l,r,e);
    if(r>mid) update(root<<1|1,l,r,e);
    upsh(root);
}
int ans;
void querty(int root,int e){
    if(d[root].l==d[root].r){
      ans=d[root].t;
      return ;
    }
    uppush(root);
    int mid=(d[root].l+d[root].r)>>1;
    if(e<=mid) querty(root<<1,e);
    else querty(root<<1|1,e);
    upsh(root);
}
int main(){
    int n,m;
    int Case=0;
    while(scanf("%d%d",&n,&m)==2){
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        built(1,1,n);
        int t1,t2;
        if(Case++!=0) puts("");
        for(int i=1;i<=m;i++){
            scanf("%d%d",&t1,&t2);
            querty(1,t1);
            int ans1=ans;
            querty(1,t1+1);
            int ans2=ans;
            int t=(t2-((180+ans2-ans1+360)%360))%360;
            update(1,t1+1,n,t);
            printf("%.2lf %.2lf\n",d[1].t1,d[1].t2);
        }
    }
    return 0;
}

  

时间: 2024-10-10 03:21:49

各种骚操作线段树的相关文章

COGS 2638. 数列操作ψ 线段树

传送门 : COGS 2638. 数列操作ψ 线段树 这道题让我们维护区间最大值,以及维护区间and,or一个数 我们考虑用线段树进行维护,这时候我们就要用到吉司机线段树啦 QAQ 由于发现若干次and,or之后,如果数据分布均匀,那么几乎所有的数在若干次操作后都会变成同一个数 因为我们的and操作中的0位,以及or操作当中的1位,都是可以把整个区间的那一二进制位重置为相同的 我们考虑利用这一个性质 如果我们直接维护一个区间内的值是否是相同的,那么效果会差很多. 我们发现我们在进行and操作的时

bzoj 2962 序列操作(线段树)

题外话 做这道题我整个人都非常的绝望,推了一会发现是线段树裸题,然后调了N久一直是WA 情况是这样的 开始WA的几百毫秒的都是由于我比较SB造成的,可是跑了10几秒的程序我查了N久也查不出错 最后灵机一动把50000改成60000就过了,也不知道为啥T_T Description 一个长度为n的序列,有3种操作 1:区间加c 2:区间取为相反数 3:询问区间中选择c个数相乘的所有方案的和mod19940417的值 Solution 这个操作3非常鬼畜,似乎没啥好的办法,但是仔细推导一番会发现这个

bzoj 1858 序列操作(线段树)

题外话 本来想练练线段树的,然后发现这题及其蛋疼,要打一坨标记,这是我写过的最长的线段树了= = 然后我很SB的把R打成了r调了一个下午真是蛋疼QvQ Description: 给定一个0/1序列,有如下5个操作: 0:区间赋值为0 1:区间赋值为1 2:区间取反 3:询问区间内1的个数 4:询问区间内最大连续1的个数 Solution 没有操作4这显然就是个SB题,有了操作4我们需要打几个标记 我们需要从左端点开始连续0/1的个数,从右端点开始的连续0/1个数 区间内0/1个数,区间内最大连续

BZOJ 1858 SCOI2010 序列操作 线段树

题目大意:给定一个01序列,提供三种操作: 0:把一段区间的全部元素都变成0 1:把一段区间的全部元素都变成1 2:把一段区间内的全部元素全都取反 3:查询一段区间内1的个数 4:查询一段区间内最长的一段连续的1 首先假设没有操作4这就是bitset的水题... 多了这个,我们考虑线段树 线段树的每个节点存改动标记和翻转标记,以及该区间的信息 尽管查询的信息都是1 可是我们要连0一起保存 由于翻转后0就变成了1 1就变成了0 区间信息包含: 左端点的元素 右端点的元素 左端点開始的最长的连续元素

bzoj 2333 棘手的操作(线段树)

题外话 昨天粗去浪了一天,打麻将输了一下午真是拙计啊 这个题号2333真是2333,有爱无比 晚上回家把这题写了,1A,还算不错 Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y:加一条边,连接第x个节点和第y个节点 A1 x v:将第x个节点的权值增加v A2 x v:将第x个节点所在的连通块的所有节点的权值都增加v A3 v:将所有节点的权值都增加v F1 x:输出第x个节点当前的权值 F2 x:

【BZOJ-1858】序列操作 线段树

1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1961  Solved: 991[Submit][Status][Discuss] Description lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区

bzoj1858: [Scoi2010]序列操作 线段树

线段树,维护七个值两个标记. 用结构体快了一倍…… #include<bits/stdc++.h> #define N (1<<18) #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k #define L l,M,P #define R M+1,r,S #define Z int l=0,int r=n-1,int k=1 using namespace

神奇的操作——线段树合并(例题: BZOJ2212)

什么是线段树合并? 首先你需要动态开点的线段树.(对每个节点维护左儿子.右儿子.存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点.) 考虑这样一个问题: 你现在有两棵权值线段树(大概是用来维护一个有很多数的可重集合那种线段树,若某节点对应区间是\([l, r]\),则它存储的数据是集合中\(\ge l\).\(\le r\)的数的个数),现在你想把它们俩合并,得到一棵新的线段树.你要怎么做呢? 提供这样一种算法(tree(x, y, z)表示一个左儿子是x.右儿子是y.数据是z的

bzoj1858 [Scoi2010]序列操作——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1858 线段树...调了一个上午...(后面带 // 的都是改出来的) lazy 标记的下放好麻烦,还得考虑赋值和取反的先后顺序什么的... 因为在取反时把赋值标记 swap 了,所以下放的时候先判断取反再判断赋值... 而且WA了一上午的原因竟然是一开始不慎把取反以为成翻转了,后来没改干净...那个 rev 的名字啊... 总之没有太改变自己最初的想法.改了些细节就A了还是很高兴的! 代码