1. 程式人生 > >無旋Treap——從入門到放棄

無旋Treap——從入門到放棄

前言

已經是從入門到放棄的第四篇了。
但是本文並不打算給大家講無旋Treap複雜度證明一類的。
很顯然每次操作都是期望Olog(n)

什麼是Treap?

Treap=Tree+heap
其核心思想在於在權值上維護一棵二叉查詢樹,在優先順序上維護一個堆
有旋treap利用旋轉操作來維護堆的性質,
而無旋treap利用有序構樹維護堆的性質。

無旋Treap的兩大構樹順序:權值與原排列

權值構樹是很常見的一種構樹方法。和有旋treap一樣,左兒子與右兒子分別表示比自己小或比自己大的樹,同時其優先順序均低於該節點。這類問題用有旋treap也能夠很好地解決。這樣的題有很多,比如bzoj3224普通平衡樹
但很不幸的是,很多與平衡樹沾邊的題目大多有維護一個原有有序序列的要求,這個要求基本上就把有旋treap幹掉了。但是對於無旋treap,我們可以按原有的序列進行構樹,這樣就可以維護一個原有的有序排列了。

無旋treap的核心操作:split與merge

但是在構樹之後肯定是有修改操作的。這點是毋庸置疑的。對於有旋treap,我們可以通過旋轉來插入要加入的權值或是刪除對應的節點,但對於可能需要進行區間操作的無旋treap,我們顯然不能直接這樣做。
此時,因為無旋treap的有序性,我們可以像一般的有旋treap一樣對需要
插入/刪除的部分進行定位查詢。
如果是對於權值有序,像普通的有旋treap一樣直接遞迴查詢即可,
如果是對於原序列有序,則維護一組指標,也可以很方便地進行查詢。
那麼在查詢到了相應的樹中位置之後,我們需要做的就是——
把這棵樹拆開

split

整個無旋treap的核心就是它的有序性。
在找到了需要操作的位置後,我們可以把這棵樹拆成兩棵有序的樹。
對於查詢到的節點,我們根據該節點左兒子的大小對這個節點應該在哪棵樹進行判斷,然後遞迴處理即可。

inline D split(Treap* pos,int k){
    if(pos==NULL) return D(NULL,NULL);
    D y;
    if(sze(pos->son[0])>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-sze(pos->son[0]));
        pos
->son[1]=y.first; pos->update(); y.first=pos; } return y; }

merge

在你進行了加入/刪除操作之後,你發現你手上現在有兩到三棵樹了,自然我們需要將這些樹合併。合併的具體操作也是遞迴處理,此時就可以維護無旋treap的堆性質。
但是需要注意的是,合併時兩棵樹的左右位置,維護的是整棵樹的有序性。

inline Treap* merge(Treap* a,Treap* b){
    if(!a) return b;
    if(!b) return a;
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}

水得如壺口瀑布一樣的水題

(1)bzoj3224:普通平衡樹
題面見連結 傳送門
最簡單最基礎的權值排序外加單點修改查詢

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
    int i=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())
        if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar())
        i=(i<<3)+(i<<1)+(ch^48);
    return i*f;
}
int buf[1024];
inline void write(int x){
    if(!x){putchar('0');return ;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10,x/=10;}
    while(buf[0]) putchar(buf[buf[0]--]+48);
    return ;
}
struct Treap{
    Treap* son[2];
    int weight,sze,data;
    Treap(int v){
        sze=1,data=v;weight=rand();
        son[1]=son[0]=NULL;
        return ;
    }
    inline void update(){
        sze=1+(son[0]!=NULL?son[0]->sze:0)+(son[1]!=NULL?son[1]->sze:0);
        return ;
    }
}*root;
typedef pair<Treap*,Treap*>D;
inline int sze(Treap* pos){
    return pos?pos->sze:0;
}
int n,ord,x;
inline Treap* merge(Treap* a,Treap* b){
    if(!a) return b;
    if(!b) return a;
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}
inline D split(Treap* pos,int k){
    if(pos==NULL) return D(NULL,NULL);
    D y;
    if(sze(pos->son[0])>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-sze(pos->son[0]));
        pos->son[1]=y.first;
        pos->update();
        y.first=pos;
    }
    return y;
}
inline int getrank(Treap* pos,int x){
    if(pos==NULL) return 0;
    else return (pos->data>=x)?getrank(pos->son[0],x):getrank(pos->son[1],x)+1+sze(pos->son[0]);
}
inline int getkth(int k){
    D x=split(root,k-1);
    D y=split(x.second,1);
    Treap* pos=y.first;
    root=merge(x.first,merge(pos,y.second));
    return pos==NULL?0:pos->data;
}
inline void insert(int d){
    int k=getrank(root,d);
    D x=split(root,k);
    Treap* pos=new Treap(d);
    root=merge(x.first,merge(pos,x.second));
    return ;
}
inline void remove(int d){
    int k=getrank(root,d);
    D x=split(root,k);
    D y=split(x.second,1);
    root=merge(x.first,y.second);
    return ;
}
signed main(){
    n=read();
    for(int i=1;i<=n;++i){
        ord=read();x=read();
        switch(ord){
            case 1:insert(x);break;
            case 2:remove(x);break;
            case 3:write(getrank(root,x)+1);puts("");break;
            case 4:write(getkth(x));puts("");break;
            case 5:write(getkth(getrank(root,x)));puts("");break;
            case 6:write(getkth(getrank(root,x+1)+1));puts("");break;
        }
    }
    return 0;
}

(2)bzoj1503 鬱悶的出納員
題面依然見連結點這裡
很明顯你不能直接進行全域性修改對伐。這個時候你需要打一個差分。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
    int i=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())
        if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar())
        i=(i<<3)+(i<<1)+(ch^48);
    return i*f;
}
int buf[1024];
inline void write(int x){
    if(!x){putchar('0');return ;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10,x/=10;}
    while(buf[0]) putchar(buf[buf[0]--]+48);
    return ;
}
#define stan 11
struct Treap{
    Treap* son[2];
    int sze,val,weight;
    Treap(){val=sze=0,weight=rand();son[1]=son[0]=NULL;}
    inline void update(){
        sze=1+son[0]->sze+son[1]->sze;
    }
}*null=new Treap,*root=null;
typedef pair<Treap*,Treap*> D;
int m,mini,tmp=0,x,cnt;
char ord[stan];
inline Treap* newtreap(int x){
    Treap* pos=new Treap();
    pos->son[0]=pos->son[1]=null;
    pos->sze=1;pos->val=x;
    return pos;
}
inline Treap* merge(Treap* a,Treap* b){
    if(a==null) return b;
    if(b==null) return a;
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}
inline D split(Treap* pos,int k){
    if(pos==null) return D(null,null);
    D y;
    if(pos->son[0]->sze>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-pos->son[0]->sze);
        pos->son[1]=y.first;
        pos->update();
        y.first=pos;
    }
    return y;
}
inline int getrank(Treap* pos,int x){
    if(pos==null) return 0;
    return (pos->val>=x)?getrank(pos->son[0],x):getrank(pos->son[1],x)+1+pos->son[0]->sze;
}
inline int getkth(int k){
    D x=split(root,k-1);
    D y=split(x.second,1);
    Treap* pos=y.first;
    root=merge(x.first,merge(pos,y.second));
    return pos->val;
}
inline void insert(int d){
    int k=getrank(root,d);
    D x=split(root,k);
    Treap* pos=newtreap(d);
    root=merge(x.first,merge(pos,x.second));
    return ;
}
inline void remove(){
    D x=split(root,1);
    root=x.second;
    ++cnt;
    return ;
}
signed main(){
    m=read();mini=read();
    while(m--){
        scanf("%s",ord);x=read();
        switch(ord[0]){
            case 'I':if(x>=mini) insert(x-tmp);break;
            case 'F':{
                if(root==null||root->sze<x)
                    puts("-1");
                else{
                    write(getkth(root->sze-x+1)+tmp);
                    puts("");
                }
                break;
            }
            case 'A':tmp+=x;break;
            case 'S':{
                tmp-=x;
                while(root!=null&&getkth(1)+tmp<mini)
                    remove();
                break;
            }
        }
    }
    write(cnt);
    return 0;
}

(3)bzoj3223文藝平衡樹
題面照例見連結點我點我
基礎的區間翻轉操作,像線段樹一樣打標記即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
    int i=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())
        if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar())
        i=(i<<3)+(i<<1)+(ch^48);
    return i*f;
}
int buf[1024];
inline void write(int x){
    if(!x){putchar('0');return ;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10,x/=10;}
    while(buf[0]) putchar(buf[buf[0]--]+48);
    return ;
}
#define stan 555555
struct Treap{
    Treap* son[2];
    int val,weight,sze;bool flip; 
    Treap(){
        val=-999999999;sze=0;weight=rand();flip=false;
        return ;
    }
    inline void update(){
        sze=son[1]->sze+son[0]->sze+1;
        return ;
    }
}*null=new Treap(),*root=null,*stack[stan],*x,*last;
typedef pair<Treap*,Treap*> D;
int n,m,sta,en;
inline void maintain_flip(Treap* pos){
    if(pos==null) return ;
    pos->flip^=1;
    return ;
}
inline void pushdown(Treap* pos){
    if(pos==null) return ;
    if(pos->flip){
        pos->flip^=1;
        maintain_flip(pos->son[0]);
        maintain_flip(pos->son[1]);
        swap(pos->son[0],pos->son[1]);
    }
    return ;
}
inline Treap* newtreap(int val){
    Treap *pos=new Treap();
    pos->son[1]=pos->son[0]=null;pos->weight=rand();
    pos->val=val;pos->sze=1;pos->flip=0;
    return pos;
}
inline Treap* merge(Treap* a,Treap* b){
    if(a==null) return b;
    if(b==null) return a;
    pushdown(a);pushdown(b);
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}
inline D split(Treap* pos,int k){
    if(pos==null) return D(null,null);
    D y;pushdown(pos);
    if(pos->son[0]->sze>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-pos->son[0]->sze);
        pos->son[1]=y.first;
        pos->update();
        y.first=pos;
    }
    return y;
}
inline Treap* build(){
    int p=0;
    for(int i=1;i<=n;++i){
        x=newtreap(i);last=null;
        while(p&&stack[p]->weight>x->weight){
            stack[p]->update();
            last=stack[p];
            stack[p--]=null;
        }
        if(p) stack[p]->son[1]=x;
        x->son[0]=last;stack[++p]=x;
    }
    while(p) stack[p--]->update();
    return stack[1];
}
inline void reverse(){
    sta=read();en=read();
    D x=split(root,sta-1);
    D y=split(x.second,en-sta+1);
    maintain_flip(y.first);
    root=merge(x.first,merge(y.first,y.second));
    return ;
}
inline void write_in_order(Treap* pos){
    if(pos==null) return;
    pushdown(pos);
    write_in_order(pos->son[0]);
    write(pos->val);putchar(' ');
    write_in_order(pos->son[1]);
    return ;
}
signed main(){
    n=read();m=read();
    root=build();
    while(m--) reverse();
    write_in_order(root); 
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
    int i=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())
        if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar())
        i=(i<<3)+(i<<1)+(ch^48);
    return i*f;
}
int buf[1024];
inline void write(int x){
    if(!x){putchar('0');return ;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10,x/=10;}
    while(buf[0]) putchar(buf[buf[0]--]+48);
    return ;
}
typedef unsigned int uint;
inline uint nextUint() {
    static uint seed = 19260817;
    seed ^= seed << 13;
    seed ^= seed >> 17;
    seed ^= seed << 5;
    return seed;
}
#define stan 88888
#define sten 11
struct Treap{
    Treap* son[2];
    Treap* fa;
    int sze,val;
    uint weight;
    Treap(){val=-999999999,sze=0,weight=nextUint(),son[0]=son[1]=fa=this;}
    inline void update(){
        sze=son[0]->sze+son[1]->sze+1;
        son[0]->fa=son[1]->fa=this;
    }
}*null=new Treap(),*root=null,*stack[stan],*x,*last,*posi[stan];
typedef pair<Treap*,Treap*> D;
int n,m,s,u,a[stan];
char ord[sten];
inline Treap* newtreap(int val){
    Treap* pos=new Treap();
    pos->son[1]=pos->son[0]=pos->fa=null;
    pos->sze=1;pos->val=val;pos->weight=nextUint();
    return pos;
}
inline Treap* merge(Treap* a,Treap* b){
    if(a==null) return b;
    if(b==null) return a;
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}
inline D split(Treap* pos,int k){
    if(pos==null) return D(null,null);
    D y;
    if(pos->son[0]->sze>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-pos->son[0]->sze);
        pos->son[1]=y.first;
        pos->update();
        y.first=pos;
    }
    return y;
}
inline Treap* build(){
    int p=0;
    for(int i=1;i<=n;++i){
        a[i]=read();
        posi[a[i]]=x=newtreap(a[i]);last=null;
        while(p&&stack[p]->weight>x->weight){
            stack[p]->update();
            last=stack[p];
            stack[p--]=null;
        }
        if(p) stack[p]->son[1]=x;x->fa=stack[p];
        x->son[0]=last;last->fa=x;
        stack[++p]=x;
    }
    while(p) stack[p--]->update();
    return stack[1];
}
inline int getrank(Treap* pos){
    int ret=pos->son[0]->sze;
    while(pos->fa!=null&&pos->fa!=NULL){
        if(pos==pos->fa->son[1]) ret+=pos->fa->son[0]->sze+1;
        pos=pos->fa;
    }
    return ret;
}
inline void addhead(int x){
    int k=getrank(posi[x]);
    D X=split(root,k);
    D y=split(X.second,1);
    root=merge(y.first,merge(X.first,y.second));
    return ;
}
inline void addtail(int x){
    int k=getrank(posi[x]);
    D X=split(root,k);
    D y=split(X.second,1);
    root=merge(X.first,merge(y.second,y.first));
    return ;
}
void addmid(int xxx,int opt) {  
    if(!opt) return ;
    static D X,y,z;  
    int k=getrank(posi[xxx]);  
    X=split(root, k);  
    y=split(X.second,1);  
    if(opt==-1)X=split(X.first,X.first->sze-1), root=merge(merge(X.first,y.first),merge(X.second,y.second));  
    else z=split(y.second,1), root=merge(merge(X.first,z.first),merge(y.first,z.second));  
}  
inline int getkth(int k){
    D X=split(root,k-1);
    D y=split(X.second,1);
    Treap* pos=y.first;
    root=merge(X.first,merge(pos,y.second));
    return pos->val;
}
signed main(){
    n=read();m=read();
    root=build();
    while(m--){
        scanf("%s",ord);s=read();
        switch(ord[0]){
            case 'T':addhead(s);break;
            case 'B':addtail(s);break;
            case 'I':u=read();addmid(s,u);break;
            case 'A':write(getrank(posi[s]));puts("");break;
            case 'Q':write(getkth(s));puts("");break;
        }
    }
    return 0;
}

(5)CQOI2014排序機械臂
題面還是有連結就是這裡
很中規中矩的不固定排列序+區間修改

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
    int i=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())
        if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar())
        i=(i<<3)+(i<<1)+(ch^48);
    return i*f;
}
int buf[1024];
inline void write(int x){
    if(!x){putchar('0');return ;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10,x/=10;}
    while(buf[0]) putchar(buf[buf[0]--]+48);
    return ;
}
typedef unsigned int uint;
inline uint nxtunit(){
    static uint seed=19260817;
    seed^=seed<<13;
    seed^=seed>>17;
    seed^=seed<<5;
    return seed;
}
#define stan 111111
struct Treap{
    Treap* son[2];
    Treap* fa;
    int sze,val;
    uint weight;
    bool flip;
    Treap(){val=-999999999,sze=0,weight=nxtunit(),son[0]=son[1]=fa=this;flip=false;}
    inline void update(){
        sze=son[0]->sze+son[1]->sze+1;
        son[0]->fa=son[1]->fa=this;
    }
}*null=new Treap(),*root=null,*stack[stan],*x,*last,*posi[stan];
typedef pair<Treap*,Treap*> D;
int n,m,s,u,a[stan],to[stan];
struct thing{
    int ord,val;
}thi[stan];
inline bool cmp(const thing &a,const thing &b){
    if(a.val!=b.val) return a.val<b.val;
    else return a.ord<b.ord;
}
inline Treap* newtreap(int val){
    Treap* pos=new Treap();
    pos->son[1]=pos->son[0]=pos->fa=null;
    pos->sze=1;pos->val=val;pos->weight=nxtunit();
    pos->flip=false;
    return pos;
}
inline void pushdown(Treap* pos){
    if(pos==null||pos==NULL) return ;
    if(pos->flip){
        pos->flip^=1;
        pos->son[0]->flip^=1;
        pos->son[1]->flip^=1;
        swap(pos->son[0],pos->son[1]);
    }
    return ;
}
inline Treap* merge(Treap* a,Treap* b){
    if(a==null) return b;
    if(b==null) return a;
    pushdown(a);pushdown(b);
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}
inline D split(Treap* pos,int k){
    if(pos==null) return D(null,null);
    D y;pushdown(pos);
    if(pos->son[0]->sze>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-pos->son[0]->sze);
        pos->son[1]=y.first;
        pos->update();
        y.first=pos;
    }
    return y;
}
inline void rotate(Treap* pos){
    if(pos==null||pos==NULL) return ;
    rotate(pos->fa);
    pushdown(pos);
    return ; 
}
inline int getrank(Treap* pos){
    rotate(pos);
    int ret=pos->son[0]->sze+1;
    while(pos->fa!=null&&pos->fa!=NULL){
        if(pos==pos->fa->son[1]) ret+=pos->fa->son[0]->sze+1;
        pos=pos->fa;
    }
    return ret;
}
inline int getans(int d){
    int ret=getrank(posi[d]);
    D x=split(root,ret);
    D y=split(x.first,d-1);
    y.second->flip^=1;
    root=merge(merge(y.first,y.second),x.second);
    return ret;
}
signed main(){
    n=read();
    for(int i=1;i<=n;++i){
        a[i]=read();thi[i].ord=i;thi[i].val=a[i];
    }
    sort(thi+1,thi+n+1,cmp);
    for(int i=1;i<=n;++i)
        to[thi[i].ord]=i;
    for(int i=1;i<=n;++i){
        posi[to[i]]=newtreap(a[i]);
        root=merge(root,posi[to[i]]);
    }
    for(int i=