[NOI2005]维护数列

题解 [NOI2005]维护数列

关于fhq_treap的大小分裂应用非常巧妙的一道题。a了之后可以大大加深对花Qtreap的理解

[NOI2005]维护数列

建树

建树就是一个考点,总不能n次merge吧。利用到了treap的线性构造。欢迎在本博客上阅读《数据结构 线性构造treap》

插入

这个嘛,线性构造一个treap,然后插入即可:

void insert(int k){
        root=merge(root,newnode(k));
    }

删除

 void delate(int pos,int len){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        root=merge(x,z);
        recycle(y);
    }

修改

分裂之后打标记,在每一次merge与split时下放即可

当初理解这个做法的时候花了一点时间:因为有点怀疑,直接将标记打在节点上,会不会标记下放的时候放到了不该放的地方?

自然不会。如果在split的时候遇到了标记,说明标记是针对全体的,自然下放;如果在merge的时候遇到了标记,说明标记是针对于各自区间的,在merge之前各自下放到自己的区间,就不怕merge之后标记混乱

void turn(int pos,int len,int k){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        lazy_turn[y]=k;
        x=merge(x,y); root=merge(x,z);
    }

反转

同样利用到标记。有标记的话反转两个儿子即可

void reverse(int pos,int len){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        lazy_rev[y]^=1;
        x=merge(x,y); root=merge(x,z);
    }

增添上下放标记的特殊分裂和合并

下放标记:

void pushdown(int k){
        if(lazy_turn[k]!=INF){
            val[k]=lazy_turn[k];
            sum[k]=lazy_turn[k]*size[k];
            if(lazy_turn[k]>=0) sum_pre[k]=sum_suf[k]=sum_tot[k]=sum[k];
            else sum_pre[k]=sum_suf[k]=sum_tot[k]=val[k];
            lazy_turn[ch[k][0]]=lazy_turn[k];
            lazy_turn[ch[k][1]]=lazy_turn[k];
            lazy_turn[k]=INF;
        }
        if(lazy_rev[k]){
            swap(sum_suf[k],sum_pre[k]);
            swap(ch[k][0],ch[k][1]);
            lazy_rev[ch[k][0]]^=1;
            lazy_rev[ch[k][1]]^=1;
            lazy_rev[k]=0;
        }
    }

分裂:

void split(int now,int k,int &x,int &y){
        if(!now) x=y=0;
        else{
            pushdown(now);
            int t=size[ch[now][0]]+1;
            if(t<=k) {x=now; split(ch[now][1],k-t,ch[now][1],y);}
            else {y=now; split(ch[now][0],k,x,ch[now][0]);}
            updata(now);
        }
    }

合并:

 int merge(int a,int b){
        pushdown(a); pushdown(b);
        if(!a||!b){
            pushdown(a+b);
            updata(a+b);
            return a+b;
        }
        if(rad[a]<rad[b]){
            if(lazy_rev[a]||lazy_turn[a]!=INF) pushdown(a);
            ch[a][1]=merge(ch[a][1],b); updata(a); return a;
        }
        else{
            if(lazy_rev[b]||lazy_turn[b]!=INF) pushdown(b);
            ch[b][0]=merge(a,ch[b][0]); updata(b); return b;
        }
    }

区间求和

简单维护一下sum即可:

int qsum(int pos,int len){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        int ans=sum[y];
        x=merge(x,y); root=merge(x,z);
        return ans;
    }

最大子段和

这是一个难点

设sum_pre[k]表示k区间紧挨左面的最大字段和,同理sum_suf[k]为紧靠右面的;sum_tot[k]为区间最大字段和。那么把updata稍作修改

\[sum_{pre}[k]=max(sum_{pre}[lson],sum[lson]+val[k],sum[lson]+val[k]+sum_{pre}[lson] \]

\[sum_{suf}[k]=max(sum_{suf}[rson],val[k]+sum[rson]+sum_{suf}[lson]+val[k]+sum[rson] \]

\[sum_{tot}[k]=max(val[k],sum_{tot}[lson],sum_{tot}[rson],sum_{suf}[lson]+val[k],val[k]+sum_{pre}[rson],sum_{suf}[lson]+val[k]+sum_{pre}[rson] \]

每一次updata的时候更新一边:

void upd(int &n,int k){
        n=max(n,k);
    }

    void updata(int k){
        int lson=ch[k][0],rson=ch[k][1];
        
        pushdown(lson);
        pushdown(rson);

        size[k]=1+size[lson]+size[rson];
        sum[k]=val[k]+sum[lson]+sum[rson];

        sum_pre[k]=sum_suf[k]=(size[k]==1?val[k]:-INF); sum_tot[k]=val[k];
        upd(sum_pre[k],sum_pre[lson]);
        upd(sum_pre[k],max(sum[lson]+val[k],sum[lson]+val[k]+sum_pre[rson]));
        upd(sum_suf[k],sum_suf[rson]);
        upd(sum_suf[k],max(sum[rson]+val[k],sum_suf[lson]+val[k]+sum[rson]));
        upd(sum_tot[k],val[k]);
        upd(sum_tot[k],max(sum_tot[lson],sum_tot[rson]));
        upd(sum_tot[k],max(sum_suf[lson]+val[k],sum_pre[rson]+val[k]));
        upd(sum_tot[k],sum_suf[lson]+val[k]+sum_pre[rson]);
    }

空间

这道题的空间很吃紧,需要利用到内存池思想

开一个队列记录所有没有使用过的编号。每一次需要增加点的时候从队列中取出来一个;遇到需要回收的子树,就暴力枚举一遍,插到队列中,作为新的可以使用的编号

void recycle(int k){
        line.push(k);
        if(ch[k][0]) recycle(ch[k][0]);
        if(ch[k][1]) recycle(ch[k][1]);
    }

而当你把上述的一切的一切全部考虑充分,你就成功切了一道NOI难度的数据结构题φ(゜▽゜*)♪

完整代码:

// luogu-judger-enable-o2
#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define FT fhq_treap::

inline int read();

namespace fhq_treap{
    const int MAX=5e5+5,INF=0x3f3f3f3f;
    int tot,root,x,y,z;
    int ch[MAX][2],val[MAX],rad[MAX],size[MAX];
    int sum[MAX],sum_tot[MAX],sum_pre[MAX],sum_suf[MAX];
    int lazy_rev[MAX],lazy_turn[MAX];
    queue <int> line;

    void init(){
        for(register int i=1;i<=500000;++i) line.push(i);
    }

    void upd(int &n,int k){
        n=max(n,k);
    }

    int newnode(int k){
        tot=line.front(); line.pop();
        val[tot]=sum[tot]=sum_suf[tot]=sum_pre[tot]=sum_tot[tot]=k;
        size[tot]=1;
        rad[tot]=rand();
        lazy_turn[tot]=INF; lazy_rev[tot]=0;
        ch[tot][0]=ch[tot][1]=0;
        return tot;
    }

    void recycle(int k){
        line.push(k);
        if(ch[k][0]) recycle(ch[k][0]);
        if(ch[k][1]) recycle(ch[k][1]);
    }

    void pushdown(int k){
        if(lazy_turn[k]!=INF){
            val[k]=lazy_turn[k];
            sum[k]=lazy_turn[k]*size[k];
            if(lazy_turn[k]>=0) sum_pre[k]=sum_suf[k]=sum_tot[k]=sum[k];
            else sum_pre[k]=sum_suf[k]=sum_tot[k]=val[k];
            lazy_turn[ch[k][0]]=lazy_turn[k];
            lazy_turn[ch[k][1]]=lazy_turn[k];
            lazy_turn[k]=INF;
        }
        if(lazy_rev[k]){
            swap(sum_suf[k],sum_pre[k]);
            swap(ch[k][0],ch[k][1]);
            lazy_rev[ch[k][0]]^=1;
            lazy_rev[ch[k][1]]^=1;
            lazy_rev[k]=0;
        }
    }

    void updata(int k){
        int lson=ch[k][0],rson=ch[k][1];
        
        pushdown(lson);
        pushdown(rson);

        size[k]=1+size[lson]+size[rson];
        sum[k]=val[k]+sum[lson]+sum[rson];

        sum_pre[k]=sum_suf[k]=(size[k]==1?val[k]:-INF); sum_tot[k]=val[k];
        upd(sum_pre[k],sum_pre[lson]);
        upd(sum_pre[k],max(sum[lson]+val[k],sum[lson]+val[k]+sum_pre[rson]));
        upd(sum_suf[k],sum_suf[rson]);
        upd(sum_suf[k],max(sum[rson]+val[k],sum_suf[lson]+val[k]+sum[rson]));
        upd(sum_tot[k],val[k]);
        upd(sum_tot[k],max(sum_tot[lson],sum_tot[rson]));
        upd(sum_tot[k],max(sum_suf[lson]+val[k],sum_pre[rson]+val[k]));
        upd(sum_tot[k],sum_suf[lson]+val[k]+sum_pre[rson]);
    }

    int build(int len){
        stack <int> tmp;
        for(int i=1;i<=len;++i){
            int j=0,now=newnode(read());
            while(!tmp.empty()&&rad[now]<rad[tmp.top()]){
                ch[tmp.top()][1]=j;
                j=tmp.top();
                updata(j);
                ch[now][0]=tmp.top();
                tmp.pop();
            }
            updata(now);
            if(!tmp.empty()) ch[tmp.top()][1]=now,updata(tmp.top());
            tmp.push(now);
        }
        int u;
        while(!tmp.empty()) u=tmp.top(),updata(u),tmp.pop();
        return u;
    }

    void split(int now,int k,int &x,int &y){
        if(!now) x=y=0;
        else{
            pushdown(now);
            int t=size[ch[now][0]]+1;
            if(t<=k) {x=now; split(ch[now][1],k-t,ch[now][1],y);}
            else {y=now; split(ch[now][0],k,x,ch[now][0]);}
            updata(now);
        }
    }

    int merge(int a,int b){
        pushdown(a); pushdown(b);
        if(!a||!b){
            pushdown(a+b);
            updata(a+b);
            return a+b;
        }
        if(rad[a]<rad[b]){
            if(lazy_rev[a]||lazy_turn[a]!=INF) pushdown(a);
            ch[a][1]=merge(ch[a][1],b); updata(a); return a;
        }
        else{
            if(lazy_rev[b]||lazy_turn[b]!=INF) pushdown(b);
            ch[b][0]=merge(a,ch[b][0]); updata(b); return b;
        }
    }

    void original(int len){
        root=build(len);
    }

    void insert(int k){
        root=merge(root,newnode(k));
    }

    void delate(int pos,int len){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        root=merge(x,z);
        recycle(y);
    }

    int qsum(int pos,int len){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        int ans=sum[y];
        x=merge(x,y); root=merge(x,z);
        return ans;
    }

    void reverse(int pos,int len){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        lazy_rev[y]^=1;
        x=merge(x,y); root=merge(x,z);
    }

    void turn(int pos,int len,int k){
        split(root,pos-1,x,y);
        split(y,len,y,z);
        lazy_turn[y]=k;
        x=merge(x,y); root=merge(x,z);
    }

    int maxline(){
        return sum_tot[root];
    }

    void getin(int pos,int tot){
        split(root,pos,x,y);
        // for(int i=1;i<=tot;++i) x=merge(x,newnode(read()));
        x=merge(x,build(tot));
        root=merge(x,y);
    }

    void debug(int k){
        cout<<"This is No."<<k<< " checking.."<<endl;
        for(int i=1;i<=size[root];++i) printf("%d ",qsum(i,1)); printf("\n"); 
        // for(int i=1;i<=size[root];++i) qsum(i,1); 
        printf("\n");
    }
}	

int n,m;

int main(){
    #ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    #endif

    n=read(); m=read(); FT init();

    FT original(n);

    for(register int i=1;i<=m;++i){
        char type[10]; int a,b,c; cin>>type;
        switch(type[0]){
            case 'I':
            a=read(); b=read();
            FT getin(a,b);
            break;

            case 'D':
            a=read(); b=read();
            FT delate(a,b);
            break;

            case 'M':
            if(type[2]=='K'){
                a=read(); b=read(); c=read();
                FT turn(a,b,c);
            }
            else{
                printf("%d\n",FT maxline());
            }
            break;

            case 'R':
            a=read(); b=read();
            FT reverse(a,b);
            break;

            case 'G':
            a=read(); b=read();
            printf("%d\n",FT qsum(a,b));
            break;
        }
        // FT debug(i);
    }

    return 0;
}

inline int read(){
    char tmp=getchar(); int sum=0; bool flag=false;
    while(tmp<'0'||tmp>'9'){
        if(tmp=='-') flag=true;
        tmp=getchar();
    }
    while(tmp>='0'&&tmp<='9'){
        sum=(sum<<1)+(sum<<3)+tmp-'0';
        tmp=getchar();
    }
    return flag?-sum:sum;
}
posted @ 2020-06-29 22:33  ticmis  阅读(128)  评论(0编辑  收藏  举报