zoukankan      html  css  js  c++  java
  • [NOI2005]维护数列——平衡树观止

    本题题解并不详细,不推荐大家看这一篇。

    可以看这篇

    题目描述

    请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格)

    100%的数据中,任何时刻数列中最多含有 500 000 个数。

    100%的数据中,任何时刻数列中任何一个数字均在[-1 000, 1 000]内。

    100%的数据中,M ≤20 000,插入的数字总数不超过 4 000 000 。

    题解:

    刷完普通平衡树,文艺平衡树板子后,本题纯靠自己想,用了3h打码,4h调试,共7h多终于AC了。

    此题我认为堪称“平衡树观止”

    题目就是这么地裸。但就是让你写不出来。

    粗略地说一下splay题解:

    要用到:

    1.n+2个点,1,n+2是方便提取用的。

    2.哨兵0号点,

    3.每个点维护:fa,ch[2],sz,sum,val,lm,rm,ans最后三个是最大子段和用的。

    和标记:chan,rev

    4.用long long

    知识点:

    1.区间提取,l-1到根,r+1到根的右儿子。根右儿子的左子树就是操作区间。

    2.区间打标记,直接打。

    3.找区间左端点,右端点等,返回第k大位置节点编号。

    显然不能直接把端点当做编号。因为根本不知道原始编号转到哪里去了。

    4.pushdown时机:

    pushdown本质是为了将序列保持真正的情况,所以一旦触及,必须pushdown,否则形态一变,子树一换,都完了。

    每次查找kth的x,因为每次查找对应一次splay,必须一路上都pushdown,

    并且影响的只有rt到x路径上的点,所以其他位置可以不用pushdown

    子树内部也是不会动的,不用pushdown。

    5.pushup时机:

    本质是为了更新信息,所以一个节点变化了,或者其子树变化了,都要pushup

    splay里面有,也是路径上改变的点都会pushup的。

    另外,操作区间操作后,该子树可能有了变化,还要pushup(根右儿子),pushup(根),别的节点也不会被这次操作影响。

    6.1号点和n+2号点。为了不影响答案(可能是负数),所以权值为-inf,并且这两个点一定要保证在区间的两端。

    7.0号哨兵

    因为我这里是钦定lm,rm,ans都必须选择一个。所以0号的rm,lm,ans都是-inf,才不会因为设成0而取不到负数答案。

    8.卡空间(bzoj64MB)

    而同一时刻最多500000个。必须垃圾回收,dp[N],dc

    操作:

    1.插入:直接插成一个链可以,但是之后复杂度并不优秀。

    可以分治处理。每次插入mid递归l,mid-1和mid+1,r,数的高度就直接logn了。

    开始的加入原数列也是。

    对于新加入一些数,先像这样建成一棵树,然后类似区间提取pos,pos+1。

    那么,pos+1左儿子就是新树根的fa了,直接嫁接上去。记得pushup(pos+1)与(pos)

    见ins,pre函数

    2.删除。区间提取、然后直接删除rt的右儿子的左儿子。

    要先进入这个子树,放进回收池里。

    3.修改。区间提取,直接改。

    4.翻转。区间提取。翻转标记。

    注意,必须打标记的同时,就要翻转儿子,交换lm,rm

    因为虽然是懒标记,但是对于这个节点的影响必须处理。

    否则splay时可能会出锅。

    因为x左右儿子会有标记,但不翻转,然后rotate(x)会交换子树,pushup(y)就把没有真正正确的x曾经的子树信息记录上去了。

    5.区间和。区间提取。直接返回sum

    6.最大子段和。直接返回t[rt].ans

    注意事项:

    1.保护哨兵,保护哨兵,保护哨兵。

    2.任何时候,在splay(x)之前,必须确定x路径上的所有点都pushdown了。

    3.pushup中,t[x].ans情况较多,不要忘了可以是t[x].val

    4.swap(lm,rm)别错了。

    代码:

    #include<bits/stdc++.h>
    #define rs t[x].ch[1]
    #define ls t[x].ch[0]
    #define numb ch-'0'
    #define ri register int
    #define il inline
    using namespace std;
    typedef long long ll;
    const int N=500000+5;
    const ll inf=(1LL*1<<61);
    char ch;
    il void rdint(int &x){
        x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    il void rdll(ll &x){
        x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    struct node{
        int ch[2],fa;
        ll rm,lm,sum,ans;
        int rev;
        ll chan;
        ll val;
        int sz;
        void init(int f,ll v){
            fa=f,val=v;rm=v;lm=v,ans=v,sum=v;
            rev=0,chan=inf;
            sz=1;
        }
    }t[N];
    il ll Max(ll a,ll b){
        return a>b?a:b;
    }
    int dc,dp[N],pc;
    int n,m,rt;
    ll st[N];
    il int nc(){
        int r=dc?dp[dc--]:++pc;
        memset(t+r,0,sizeof (node));return r;
    }
    il void pushup(int x){
        t[x].sum=t[t[x].ch[0]].sum+t[t[x].ch[1]].sum+t[x].val;
        t[x].rm=Max(t[t[x].ch[1]].rm,Max(t[t[x].ch[1]].sum+t[x].val,t[t[x].ch[1]].sum+t[x].val+t[t[x].ch[0]].rm));
        t[x].lm=Max(t[t[x].ch[0]].lm,Max(t[t[x].ch[0]].sum+t[x].val,t[t[x].ch[0]].sum+t[x].val+t[t[x].ch[1]].lm));
        t[x].ans=Max(t[x].lm,t[x].rm);
        t[x].ans=Max(t[x].ans,Max(t[t[x].ch[0]].rm+t[x].val+t[t[x].ch[1]].lm,Max(t[t[x].ch[0]].rm+t[x].val,t[x].val+t[t[x].ch[1]].lm)));
        t[x].ans=Max(t[x].ans,Max(t[t[x].ch[0]].ans,t[t[x].ch[1]].ans));
        t[x].ans=Max(t[x].ans,t[x].val);//bug 2
        t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+1;
    }
    il void pushdown(int x){
        if(t[x].chan!=inf){
            if(ls){
            t[ls].chan=t[x].chan;
            t[ls].val=t[x].chan;
            t[ls].sum=t[ls].val*t[ls].sz;
            t[ls].rm=t[ls].lm=t[ls].ans=t[ls].val>0?t[ls].sum:t[ls].val;
            }
            if(rs){
            t[rs].chan=t[x].chan;
            t[rs].val=t[x].chan;
            t[rs].sum=t[rs].val*t[rs].sz;
            t[rs].rm=t[rs].lm=t[rs].ans=t[rs].val>0?t[rs].sum:t[rs].val;
            }
            t[x].chan=inf;
        }
        if(t[x].rev){
            if(ls&&t[ls].val!=-inf){
            t[ls].rev^=1;
            swap(t[ls].lm,t[ls].rm);//bug1
            }
            if(rs&&t[rs].val!=-inf){
            t[rs].rev^=1;
            swap(t[rs].lm,t[rs].rm);//bug1
            }
            swap(ls,rs);
            t[x].rev=0;
        }
    }
    il void rotate(int x){
        int y=t[x].fa,d=t[y].ch[1]==x;
        t[t[y].ch[d]=t[x].ch[!d]].fa=y;
        t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x;
        t[t[x].ch[!d]=y].fa=x;
        pushup(y);
    }
    il void splay1(int x,int f){
        while(t[x].fa!=f){
            int y=t[x].fa,z=t[y].fa;
            if(z!=f){
                rotate((t[y].ch[0]==x)==(t[z].ch[0]==y)?y:x);
            }
            rotate(x);
        }
        pushup(x);
        if(f==0) rt=x;
    }
    il int kth(int k){
        int x=rt;
        while(1){
            pushdown(x);
            int d=k-t[t[x].ch[0]].sz;
            if(d<=0) x=t[x].ch[0];
            else if(d==1) return x;
            else{
                k=d-1;
                x=t[x].ch[1];
            }
        }
    }
    int newrt;
    il void splay2(int x,int f){
        while(t[x].fa!=f){
            int y=t[x].fa,z=t[y].fa;
            if(z!=f){
                rotate((t[y].ch[0]==x)==(t[z].ch[0]==y)?y:x);
            }
            rotate(x);
        }
        pushup(x);
        if(f==0) newrt=x;
    }
    il void build(ll v){
        if(!newrt){
            newrt=nc();
            t[newrt].init(0,v);
            return;
        }
        int x=newrt;
        while(1){
            if(!t[x].ch[1]){
                t[x].ch[1]=nc();
                t[t[x].ch[1]].init(x,v);
                x=t[x].ch[1];
                break;
            }
            x=t[x].ch[1];
        }
        splay2(x,0);
    }
    il int pre(int l,int r,int f){
        if(l>r){
            return 0;
        }
        int mid=(l+r)>>1;
        int x=nc();
        t[x].init(f,st[mid]);
        t[x].ch[0]=pre(l,mid-1,x);
        t[x].ch[1]=pre(mid+1,r,x);
        pushup(x);
        return x;
    }
    il void ins(int pos,int tot){
        newrt=0;
        for(ri i=1;i<=tot;i++){
            rdll(st[i]);
        }
        newrt=pre(1,tot,0);
        int x=kth(pos+1);
        int y=kth(pos+2);
        splay1(x,0);
        splay1(y,x);
        t[y].ch[0]=newrt;
        t[newrt].fa=y;
        pushup(y);
        pushup(x);
    }
    il void del(int x){
        if(t[x].ch[0]) del(t[x].ch[0]);
        dp[++dc]=x;
        if(t[x].ch[1]) del(t[x].ch[1]);
    }
    il void remove(int pos,int tot){
        int l=kth(pos);
        int r=kth(pos+tot+1);
        splay1(l,0);
        splay1(r,l);
        del(t[r].ch[0]);
        t[r].ch[0]=0;
        pushup(r);
        pushup(l);
    }
    il void upda(int pos,int tot,ll c){
        int l=kth(pos);
        int r=kth(pos+tot+1);
        splay1(l,0);
        splay1(r,l);
        
        int x=t[r].ch[0];
        t[x].chan=c;
        t[x].val=c;
        t[x].sum=t[x].val*t[x].sz;
        t[x].rm=t[x].lm=t[x].ans=t[x].val>0?t[x].sum:t[x].val;
        
        pushup(r);
        pushup(l);
    }
    il void reverse(int pos,int tot){
        int l=kth(pos);
        int r=kth(pos+tot+1);
        splay1(l,0);
        splay1(r,l);
        
        int x=t[r].ch[0];
        t[x].rev^=1;
        swap(t[x].lm,t[x].rm);    
        
        pushup(r);
        pushup(l);
    }
    il ll qs(int pos,int tot){
        int l=kth(pos);
        int r=kth(pos+tot+1);
        splay1(l,0);
        splay1(r,l);
        
        int x=t[r].ch[0];
        int ret=t[x].sum;
        pushdown(x);//bug 3
        splay1(x,0);
        return ret;
    }
    
    int main()
    {
        t[0].lm=t[0].rm=t[0].ans=-inf;//warning warning warning !!! 
        t[0].sz=0;t[0].sum=0;
        scanf("%d%d",&n,&m);
        st[1]=-inf;
        for(int i=2;i<=n+1;i++){
            rdll(st[i]);
        }
        st[n+2]=-inf;
        
        rt=pre(1,n+2,0);
        char lp[20];
        int pos,tot;
        ll c;
        int cnt=0;
        for(ri i=1;i<=m;i++){
            ch=getchar();
            while(ch>'Z'||ch<'A')ch=getchar();
            for(cnt=0;(ch=='-')||(ch<='Z'&&ch>='A');ch=getchar())lp[++cnt]=ch;
            if(lp[1]=='I'){
                rdint(pos),rdint(tot);
                if(tot==0) continue;
                ins(pos,tot);
            }
            else if(lp[1]=='D'){
                rdint(pos),rdint(tot);
                if(tot==0) continue;
                remove(pos,tot);
            }
            else if(lp[1]=='M'&&lp[3]=='K'){
                rdint(pos),rdint(tot);rdll(c);
                if(tot==0) continue;
                upda(pos,tot,c);
            }
            else if(lp[1]=='M'&&lp[3]=='X'){
                printf("%lld
    ",t[rt].ans);
            }
            else if(lp[1]=='G'){
                rdint(pos),rdint(tot);
                if(tot==0) printf("0
    ");
                else printf("%lld
    ",qs(pos,tot));
            }
            else if(lp[1]=='R'){
                rdint(pos),rdint(tot);
                if(tot==0) continue;
                reverse(pos,tot);
            }
        }
        return 0;
    }

    总结:

    数据结构,就是利用一种结构,维护数的算法。通常为了减少时间复杂度,而牺牲一些空间作为代价。

    就是在保证查询、修改正确的情况下,最大限度地减少时间复杂度。

    为了保证复杂度,每个数据结构都有自己的方式。

    splay平衡树结构的灵魂函数:splay,rotate

    维护正确性并且节省时间的灵魂函数:pushdown,pushup

    这四个函数写对,整个平衡数的框架就建立起来了。

    将二者有机结合起来,就能够达到时间和正确性的统一。

    数据结构基本都是这样。

  • 相关阅读:
    oracle常用sql语句
    Oracle存储过程基本语法介绍
    优美的代码:do...while(0)
    半同步半异步线程池
    C11线程管理:异步操作
    C11线程管理:原子变量&单调函数
    C11线程管理:条件变量
    C11线程管理:互斥锁
    C11线程管理:线程创建
    C11关键字&字面值改善
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9643977.html
Copyright © 2011-2022 走看看