zoukankan      html  css  js  c++  java
  • 非旋Treap及可持久化平衡树

    我们知道有可持久化线段树,那么我们自然也想到平衡树是否也能可持久化,答案是肯定的。

    现在我们给出一种数据结构:Treap。 不会的点这里

    我们现在发现,旋转是一种很难持久化的操作,那么我们就要尽量避免旋转操作,我们引入以下2个操作:

       1.spilt

           对于一个Treap,我们需要把它按照第K位拆分,那应该怎么做呢?

         就像在寻找第K位一样走下去,一边走一边拆树,每次返回的时候拼接就可以了
         由于树高是logn的,所以复杂度当然也是logn的
    void spilt(T* now,int k,T* &x,T* &y){
        if (!now) {x=y=rr; return;}
        int cmp=now->s[0]?now->s[0]->siz+1:1;
        if (k<cmp) y=now,spilt(y->s[0],k,x,y->s[0]);
        else x=now,spilt(x->s[1],k-cmp,x->s[1],y);
        now->rub();
    }

        2.merge

            对于两个相对有序的Treap  若中序遍历为递增,即TreapA的最右下角也就是最大值小于TreapB的最左下角也就是最小值 ,那么Merge的复杂度是O(logn)的;

    T* merge(T* x,T* y) {
        if (!x) return y; if (!y) return x;
        if (x->val<y->val) {x->s[1]=merge(x->s[1],y);x->rub();return x;}
        y->s[0]=merge(x,y->s[0]);y->rub(); return y;
    }

    洛谷P3391 【模板】文艺平衡树(Splay)

    #pragma optimize("-O2")
    #include<bits/stdc++.h>
    #define sight(c) ('0'<=c&&c<='9')
    #define rr NULL
    #define MID ((l+r)>>1)
    using namespace std;
    inline void read(int &x){
        static char c;
        for (c=getchar();!sight(c);c=getchar());
        for (x=0;sight(c);c=getchar()) x=x*10+c-48;
    }
    void write(int x) {
        if (x<10) { putchar('0'+x); return;} write(x/10); putchar('0'+x%10);
    }
    inline void writeln(int x) {
        if (x<0) putchar('-'),x*=-1; write(x); putchar('
    ');
    }
    inline int rod(){
        static int x=23333;
        return x^=x<<13,x^=x>>17,x^=x<<5;
    }
    struct Treap{
        int val,siz,key,lazy;
        Treap* son[2];
        inline Treap(int x){
            lazy=0,siz=1; key=x; val=rod(); son[0]=son[1]=rr;
        }
        inline void Sub() {
            siz=1; 
            if (son[0]) siz+=son[0]->siz; 
            if (son[1]) siz+=son[1]->siz;
        }
        inline void pd() {
            if (!lazy) return;
            swap(this->son[0],this->son[1]);
            if (son[0]) son[0]->lazy^=1; if (son[1]) son[1]->lazy^=1; this->lazy=0;
        }
    };
    Treap* root,*x,*y,*z;
    Treap* build(int l,int r){
        if (l>r) return rr;
        Treap* S=new Treap(MID);
        S->son[0]=build(l,MID-1); S->son[1]=build(MID+1,r); 
        S->Sub();
        return S;
    }
    void split(Treap *S,int k,Treap* &x,Treap* &y){
        if (!S||k>S->siz) {x=y=rr;return;}
        S->pd();
        int cmp=(S->son[0]?S->son[0]->siz:0)+1;
        if (k<cmp) y=S,split(S->son[0],k,x,y->son[0]);
        else x=S,split(S->son[1],k-cmp,x->son[1],y);
        S->Sub();
    }
    Treap* merge(Treap* x,Treap* y){
        if (!x) return y; if (!y) return x;
        x->pd(); y->pd();
        if (x->val<y->val) {x->son[1]=merge(x->son[1],y);x->Sub();return x;}
        y->son[0]=merge(x,y->son[0]);y->Sub(); return y;
    }
    int n,m,l,r;
    void dfs(Treap *x){
        if (!x) return;
        x->pd();
        dfs(x->son[0]); 
        if (0<x->key && x->key<n+1)write(x->key),putchar(' '); 
        dfs(x->son[1]);
    }
    int main () {    
        read(n);read(m);
        root=build(1,n);
        while (m--) {
            read(l); read(r);
            split(root,l-1,x,y); 
            split(y,r-l+1,y,z);
            y->lazy^=1;
            root=merge(merge(x,y),z);
        }
        dfs(root); putchar('
    '); return 0;
    }

    洛谷P3835 【模板】可持久化平衡树

    update:突然发现洛谷此题数据很弱,原来的做法是不对的(要小小的修改一下split函数),但不知道为什么就过了。

    我们以NOI 2010 超级钢琴为例:

       我们要新建一颗树,参照主席树的做法,我们是不能在原数据上改的,这样会导致错误的统计。我们对于spilt与merge要新开一些节点,这样才是对的。

    void split(T* now,int k,T*& x,T*& y){
        if (!now) {x=y=rr;return;}
        int cmp=now->ibt();
        if (k<cmp) y=new T(),*y=*now,split(y->son[0],k,x,y->son[0]),y->red();
        else x=new T(),*x=*now,split(x->son[1],k-cmp,x->son[1],y),x->red();
    }//可持久化split操作
    T* merge(T* x,T* y){
        if (!x) return y; if (!y) return x;
        if (x->val<y->val) {
        T* X=new T();*X=*x;
        X->son[1]=merge(X->son[1],y);X->red();return X;}
        T* X=new T();*X=*y;
        X->son[0]=merge(x,X->son[0]);X->red(); return X;
    }//可持久化merge操作

    但是,如果只写这样的操作的话,对于很多题目,由于Treap的插入删除操作,我们需要多次调用split,merge操作,常数(无论是时间还是空间)都会大上好几倍,很多题显然会TLE,MLE。

    我们注意到,我们有很多的版本信息是不必存储的,比如你把树split 成x,y,加一个点再merge回去,中间这个x,y是没有可持久化的必要的,那么我们对于merge操作可以不持久化(至少我没有持久化对了,也有可能数据弱)。(但对于split操作还是要的,不然的话,还是会修改原树里面的值。)还有就是对历史版本的查询就不用持久化的操作了。

    NOI 2010 超级钢琴:题链   题解

    #pragma GCC optimize("-O2")
    #include<bits/stdc++.h>
    #define N 700007
    #define rr NULL
    using namespace std;
    struct Node{
        int key,id;
        Node(){}
        Node(int x,int y):key(x),id(y){}
        inline bool operator <(const Node A)const {
          return A.key^key?key<A.key:id<A.id;
        }
    }a[N];
    priority_queue<Node> Q;
    inline int rop(){
        static int x=23333;
        return x^=x<<13,x^=x>>17,x^=x<<5;
    }
    #define sight(c) ('0'<=c&&c<='9')
    inline void read(int &x){
        static char c;static int b;
        for (b=1,c=getchar();!sight(c);c=getchar())if (c=='-') b=-1;
        for (x=0;sight(c);c=getchar())x=x*10+c-48; x*=b;
    }
    struct T{
        int val,siz; Node key;
        T* son[2];
        T() {}
        T(Node x){
            key=x;val=rop(),siz=1;
            son[0]=son[1]=rr;}
        void red(){
            siz=1; 
            if (son[0]) siz+=son[0]->siz;
            if (son[1]) siz+=son[1]->siz;
        }
        int ibt(){return son[0]?son[0]->siz+1:1;}
    };
    int Kth(Node x,T* now){
        if (!now) return 0;
        if (now->key<x) return now->ibt()+Kth(x,now->son[1]);
        return Kth(x,now->son[0]);
    }
    void split(T* now,int k,T*& x,T*& y){
        if (!now) {x=y=rr;return;}
        int cmp=now->ibt();
        if (k<cmp) y=new T(),*y=*now,split(y->son[0],k,x,y->son[0]),y->red();
        else x=new T(),*x=*now,split(x->son[1],k-cmp,x->son[1],y),x->red();
    }
    void Split(T* now,int k,T*& x,T*& y){
        if (!now) {x=y=rr;return;}
        int cmp=now->ibt();
        if (k<cmp) y=now,Split(y->son[0],k,x,y->son[0]),y->red();
        else x=now,Split(x->son[1],k-cmp,x->son[1],y),x->red();
    }
    T* merge(T* x,T* y){
        if (!x) return y; if (!y) return x;
        if (x->val<y->val) {
        T* X=new T();*X=*x;
        X->son[1]=merge(X->son[1],y);X->red();return X;}
        T* X=new T();*X=*y;
        X->son[0]=merge(x,X->son[0]);X->red(); return X;
    }
    T* Merge(T* x,T* y){
        if (!x) return y; if (!y) return x;
        if (x->val<y->val) {x->son[1]=Merge(x->son[1],y);x->red();return x;}
        y->son[0]=Merge(x,y->son[0]);y->red();return y;
    }
    int n,k,l,r,XX,in[N],h,K;
    T* rt[N],*x,*y,*z,*t;
    long long ans;
    Node X;
    void dfs(T* x){
        if (!x) return;
        dfs(x->son[0]);
        cerr<<x->key.key<<' ';
        dfs(x->son[1]);
    }
    int main() {
        freopen("piano.in","r",stdin);
        freopen("piano.out","w",stdout);
        read(n); read(K); read(l); read(r);
        for (int i=1;i<=n;i++) read(XX),
        a[i].key=a[i-1].key+XX,a[i].id=i;
        for (int i=l;i<=r;i++) {
            k=Kth(a[i],rt[1]); Split(rt[1],k,x,y);
            rt[1]=Merge(Merge(x,new T(a[i])),y);
        } 
        for (int i=l+1,j=r+1;i<=n;i++,j++) {
            k=Kth(a[i-1],rt[i-l]); 
            split(rt[i-l],k,x,y);
            split(y,1,t,y); rt[i-l+1]=Merge(x,y);
            if (j>n) continue;
            k=Kth(a[j],rt[i-l+1]); 
            split(rt[i-l+1],k,x,y);
            rt[i-l+1]=Merge(x,Merge(new T(a[j]),y));
        }
        for (int i=l,j=r;i<=n;i++,j++) {
            Split(rt[i-l+1],rt[i-l+1]->siz-1,x,y);
            Q.push(Node(y->key.key-a[i-l].key,i)); 
            rt[i-l+1]=Merge(x,y); 
            in[i]=1;
        }
        while (K--) {
            X=Q.top();Q.pop(); 
            ans+=X.key; h=X.id;
            if (in[h]>=rt[h-l+1]->siz) continue;
            Split(rt[h-l+1],rt[h-l+1]->siz-1-in[h],x,z); Split(z,1,y,z);
            Q.push(Node(y->key.key-a[h-l].key,h));
            rt[h-l+1]=Merge(x,Merge(y,z));
            in[h]++;
        }
        printf("%lld
    ",ans);
    }
  • 相关阅读:
    tab下图片要求
    segmentController
    下拉加载更多
    获取用户信息
    时间选择器
    JavaScript标准库之 ----  Object
    JavaScript标准库之——JSON
    JavaScript标准库之
    post网络请求坑
    构造一个简单的Linux系统MenuOS
  • 原文地址:https://www.cnblogs.com/rrsb/p/8268879.html
Copyright © 2011-2022 走看看