BZOJ 1251: 序列终结者 [splay]

题意

现在需要你维护一个长度为n的序列,需要支持三种操作:

1.区间加 2.区间翻转 3.区间求最大值

题解

很明显splay裸题,复习了一下splay,感觉忘得一干二净

按下标建splay树

对于区间加和区间翻转:把l-1 splay到根,把r+1 splay到根的右儿子,然后直接对根的右儿子的左儿子打标记

做起来习惯把 [0,n+1] 转到 [1,n+2]

找的时候下传一下就好了,标记操作具体见代码

#include<cstdio>
#include<algorithm>
#define N 50010
using namespace std;

int n,m,root,sz,size[N],fa[N],ch[N][2],tag[N],v[N],mx[N],ans[N];
bool rev[N];

inline void update(int x){
    if(x){
        mx[x]=max(max(mx[ch[x][0]],mx[ch[x][1]]),v[x]);
        size[x]=1;
        if(ch[x][0]) size[x]+=size[ch[x][0]];
        if(ch[x][1]) size[x]+=size[ch[x][1]];
    }
}

inline int get(int x){return x==ch[fa[x]][1];}

inline void rotate(int x){
    int old=fa[x],oldf=fa[fa[x]],opt=get(x);
    ch[old][opt]=ch[x][opt^1],fa[ch[old][opt]]=old;
    ch[x][opt^1]=old,fa[old]=x;
    fa[x]=oldf;
    if(oldf) ch[oldf][ch[oldf][1]==old]=x;
    update(old),update(x);
}

inline void splay(int x,int rt){
    for(int fat=fa[x];fat!=rt;rotate(x),fat=fa[x])
        if(fa[fat]!=rt) rotate((get(x)==get(fat))?fat:x);
    if(!rt) root=x;
}

inline void pushdown(int x){
    if(tag[x]){
        if(ch[x][0]) v[ch[x][0]]+=tag[x],mx[ch[x][0]]+=tag[x],tag[ch[x][0]]+=tag[x];
        if(ch[x][1]) v[ch[x][1]]+=tag[x],mx[ch[x][1]]+=tag[x],tag[ch[x][1]]+=tag[x];
        tag[x]=0;
    }
    if(rev[x]) rev[ch[x][0]]^=1,rev[ch[x][1]]^=1,swap(ch[x][1],ch[x][0]),rev[x]=0;
}

void build(int l,int r,int fat){
    if(l>r) return;
    if(l==r){
        fa[l]=fat,size[l]=1,ch[fat][l>fat]=l;
        return;
    }
    int mid=l+r>>1;
    build(l,mid-1,mid),build(mid+1,r,mid);
    fa[mid]=fat,update(mid),ch[fat][mid>fat]=mid;
}

inline int find(int rt,int x){
    if (tag[rt]||rev[rt]) pushdown(rt);
    if (size[ch[rt][0]]>=x) return find(ch[rt][0],x);
    if (size[ch[rt][0]]+1==x) return rt;
    return find(ch[rt][1],x-size[ch[rt][0]]-1);
}

inline void change(int l,int r,int add){
    int x=find(root,l),y=find(root,r+2);
    splay(x,0),splay(y,x);
    int tmp=ch[y][0];
    tag[tmp]+=add,mx[tmp]+=add,v[tmp]+=add;
}

inline void change1(int l,int r){
    int x=find(root,l),y=find(root,r+2);
    splay(x,0),splay(y,x);
    rev[ch[y][0]]^=1;
}

inline int query(int l,int r){
    int x=find(root,l),y=find(root,r+2);
    splay(x,0),splay(y,x);
    return mx[ch[y][0]];
}

int main(){
    scanf("%d%d",&n,&m);mx[0]=-2e9;
    build(1,n+2,0),root=(n+3)>>1;
    int opt,l,r,v;
    while(m--){
        scanf("%d%d%d",&opt,&l,&r);
        switch(opt){
        case 1:scanf("%d",&v),change(l,r,v);break;
        case 2:change1(l,r);break;
        case 3:printf("%d\n",query(l,r));break;
        }
        }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值