[题解] BZOJ 1251 序列终结者

BZOJ 1251 序列终结者

题目描述 Description
给定一个长度为N的序列,每个序列的元素是一个整数。要支持以下三种操作:

1.将[L,R]这个区间内的所有数加上V
2.将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1
3.求[L,R]这个区间中的最大值

最开始所有元素都是0。

输入描述 Input Description
第一行两个整数N,M。M为操作个数。 以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。

输出描述 Output Description
对于每个第3种操作,给出正确的回答。

样例输入 Sample Input
4 4
1 1 3 2
1 2 4 -1
2 1 3
3 2 4

样例输出 Sample Output
2

数据范围及提示 Data Size & Hint
N<=50000,M<=100000。

分析:
BZOJ 3223 文艺平衡树升级版
与原题不同的是,这里多了区间加法,区间求最大值
根据原题的思想,我们直接将区间加打上一个lazy标记延迟更新,放在pushdown一起下传即可
而区间求最大值在更新size值时一起维护一下就好了(放在update里面)
记得打lazy标记时可以直接将最大值加上新增lazy标记的大小
每次进行区间操作时,我们只需要将l-1号节点交换到root处,将r+1号节点交换到ch[root][1]处即可保证我们要操作的序列为r+1号节点的左子树,所以对子树进行操作即可(有没有想到线段树?)
实现: 由于我们需要操作l-1和r+1,所以需要增加两个哨兵节点,将编号推至1~n+2,操作l和r+2即可。并且额外增加一个lazy数组表示延迟更新的值,增加一个mx数组表示以每个节点为根节点的子树最大值是多少。这里发现,像线段树一样,操作到哪个节点就将标记全部下传,将mx在update里面更新,lazy在pushdown里面更新。
注意,每次将区间加上x时,mx只需要直接加上x即可,不用重新更新

#include <bits/stdc++.h>
using namespace std;
#define inf 2100000000
#define maxn 50010
int ch[maxn][2],data[maxn],size[maxn],f[maxn],tag[maxn],lazy[maxn],key[maxn],mx[maxn];
int sz,root,n,m;
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
bool get(int x) {return ch[f[x]][1]==x;} //return if his father`s right kid is him
void update(int x) {
    int l=ch[x][0],r=ch[x][1];
    size[x]=1+size[l]+size[r];

    mx[x]=key[x];
    if(l) mx[x]=max(mx[x],mx[l]);
    if(r) mx[x]=max(mx[x],mx[r]);
    return ;
}
void pushdown(int x) {
    int &l=ch[x][0],&r=ch[x][1];
    if(lazy[x]) {
        if(l) lazy[l]+=lazy[x],mx[l]+=lazy[x],key[l]+=lazy[x];
        if(r) lazy[r]+=lazy[x],mx[r]+=lazy[x],key[r]+=lazy[x];
        lazy[x]=0;
    }
    if(tag[x]) {
        tag[l]^=1;
        tag[r]^=1;
        swap(l,r);
        tag[x]=0;
    }
    return ;
}
void rotate(int x) {
    int old=f[x],oldf=f[old],whichx=get(x);
    pushdown(old);
    pushdown(x);
    ch[old][whichx]=ch[x][whichx^1],f[ch[old][whichx]]=old;
    ch[x][whichx^1]=old,f[old]=x;
    f[x]=oldf;
    if(oldf)
        ch[oldf][ch[oldf][1]==old]=x;
    update(old);
    update(x);
    return ;
}
void splay(int x,int tar) { //rotate x to tar
    for(int fa;(fa=f[x])!=tar;rotate(x))
        if(f[fa]!=tar)
            rotate(get(x)==get(fa)?fa:x);
    if(!tar)
        root=x;
    return ;
}
int findx(int x) { //by search the ranking return the treenumber
    int now=root;
    while(1) {
        pushdown(now);
        if(ch[now][0] && x<=size[ch[now][0]])
            now=ch[now][0];
        else {
            int tmp=(ch[now][0]?size[ch[now][0]]:0)+1;
            if(x<=tmp) return now;
            x-=tmp;
            now=ch[now][1];
        } 
    }
}
int build(int l,int r,int fa) {
    if(l>r) return 0;
    int mid=(l+r)>>1;
    int now=++sz;
    mx[now]=key[now]=data[mid];
    f[now]=fa;
    tag[now]=0;
    ch[now][0]=build(l,mid-1,now);
    ch[now][1]=build(mid+1,r,now);
    update(now);
    return now;
}
int main() {
    n=read(),m=read();
    data[1]=-inf; 
    root=build(1,n+2,0);
    for(int i=1;i<=m;i++) {
        int cpt=read();
        int l=read(),r=read();
        l=findx(l);
        r=findx(r+2);
        switch(cpt) {
            case 1:{
                int v=read();
                splay(l,0);
                splay(r,l);
                lazy[ch[ch[root][1]][0]]+=v;
                key[ch[ch[root][1]][0]]+=v;
                mx[ch[ch[root][1]][0]]+=v;
                break;
            }
            case 2:{
                splay(l,0);
                splay(r,l);
                tag[ch[ch[root][1]][0]]^=1;
                break;
            }
            case 3:{
                splay(l,0);
                splay(r,l);
                printf("%d\n",mx[ch[ch[root][1]][0]]);
                break;
            }
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值