无旋Treap 从狂转到不转

在学习平衡树部分时,旁边的某位C姓dalao对Treap情有独钟,而我却为Splay的优美而深深着迷.这导致了对Treap的不屑一顾

这东西有什么用,那么多操作都不资瓷,low

如今繁华落尽,每次遇到需要使用平衡树的题时,不禁流下了悔恨的泪(汗)水

Splay真xx**难打!

所以,今天,就让我们来讲讲无旋Treap

无旋Treap

无旋Treap最大的优势就是编码简单,支持的操作多,相应的,它比Splay要稍稍慢一点

无旋Treap的主要操作有两个,splitmerge,能分裂 or o r 合并两棵Treap.通过这两个操作,就可以拓展出大量的其他操作

分裂

Treap的split支持在某棵Treap中分裂出大小为 num n u m 的左子树,或者是分裂出权值小于 k k 的子树
由于Treap具有二叉搜索树的性质,按size or o r key递归分裂即可

同时通过对象维护分裂后两颗treap各自的位置关系
size分裂

void split(int now,int num,int &x,int &y) {
    if(!now) x=y=0;
    else {
        pushdown(now);
        if(num<=siz[L]) {y=now;split(L,num,x,L);}
        else {x=now;split(R,num-siz[L]-1,R,y);}
        update(now);
    }
}

key分裂

void split(int now,int k,int &x,int &y) {
    if(!now) x=y=0;
    else {
        //小于k的在左子树,大于等于key的在右子树
        if(kelse {x=now;split(R,k,R,y);}
        update(now);
    }
}

合并

Treap的合并操作将两棵Treap以一定的次序组合起来
其最终形态由两方面决定:相对位置pos(我通常将其叫做自平衡权)
显然,根据Treap的原理,通常pos较小的放在靠上的位置,同时treap又是二叉搜索树,那么其相对位置决定了两者之间的父子关系(属于左子树 or o r 右子树)
类似与左偏树一样,递归合并即可

int merge(int a,int b) {
    if(a*b==0) return a+b;
    if(pos[a]1]=merge(ch[a][1],b);
        update(a);
        return a;
    }
    else {
        ch[b][0]=merge(a,ch[b][0]);
        update(b);
        return b;
    }
}

插入

通过splitmerge,我们可以简单的实现insert

按位置插入

首先将要插入的位置左侧部分分裂出来,然后依次合并左侧部分,插入节点,右侧部分即可

按权值插入

同理,将小于 v v 的节点放在左子树,依次合并

void insert(int v) {
    int a,b;
    split(rt,v,a,b);
    rt=merge(merge(a,make_node(v)),b);
}

删除

通过splitmerge,我们可以简单的实现del

按位置删除

把删除位置提取出来,合并左右节点

按权值删除

把小于等于k的节点提取出来,再小于k-1的的部分提取出来,由于可能有多等于 k k 的值,直接将等于 k k 的子树合并左右儿子节点,这样就保证了仅仅删除一个节点.若要全部删除,直接合并左右部分

void del(int v) {
    int a,b,c;
    split(rt,v,a,b);
    split(a,v-1,a,c);
    c=merge(ch[c][0],ch[c][1]);
    rt=merge(merge(a,c),b);
}

区间操作

类似于splay,先利用split提取区间,然后打上标记,最后merge

比如说,区间翻转

void rev(int l,int r) {
    split(rt,r+1,a,b);
    split(a,l,c,d);
    rev[d]^=1;
    swap(ch[d][0],ch[d][1]);
}

实践

BZOJ 3223 文艺平衡树

#include 
using namespace std;
#define L ch[now][0]
#define R ch[now][1]
#define mid ((l+r)>>1)
typedef pair <int,int> p;
const int N = 100010;
int n,m,rt;
struct treap {
    int pos[N],ch[N][2],siz[N],rev[N],rt,key[N];
    void update(int now) {siz[now]=siz[L]+siz[R]+1;}
    void Rev(int now) {swap(L,R);}
    void pushdown(int now) {if(rev[now]) {rev[now]^=1;rev[L]^=1;rev[R]^=1;Rev(L);Rev(R);}}
    int merge(int a,int b) {
        if(a*b==0) return a?a:b;
        pushdown(a);pushdown(b);
        if(pos[a]1]=merge(ch[a][1],b);
            update(a);
            return a;
        }
        else {
            ch[b][0]=merge(a,ch[b][0]);
            update(b);
            return b;
        }
    }
    void split(int now,int num,int &x,int &y) {
        if(!now) x=y=0;
        else {
            pushdown(now);
            if(num<=siz[L]) {y=now;split(L,num,x,L);}
            else {x=now;split(R,num-siz[L]-1,R,y);}
            update(now);
        }
    }
    int build(int l,int r) {
        if(l==r) {pos[l]=rand();siz[l]=1;key[l]=l-1;return l;}
        return merge(build(l,mid),build(mid+1,r));
    }
    void print(int now) {
        pushdown(now);
        if(L) print(L);
        if(key[now]>=1 && key[now]<=n) printf("%d ",key[now]);
        if(R) print(R);
    }
}t;
int read() {
    int _ans=0,_flag=1;
    char _ch=getchar();
    while((_ch != '-') && (_ch > '9' || _ch < '0')) _ch=getchar();
    if(_ch == '-') {_flag = -1;_ch = getchar();}
    while(_ch >= '0' && _ch <= '9') {_ans=_ans*10+_ch-'0';_ch=getchar();}
    return _ans*_flag;
}
int main() {
    n=read();m=read();
    rt=t.build(1,n+2);
    for(int i=1;i<=m;++i) {
        int l=read(),r=read(),a,b,c,d;
        t.split(rt,r+1,a,b);
        t.split(a,l,c,d);
        t.rev[d]^=1;
        t.Rev(d);
        rt=t.merge(c,t.merge(d,b));
    }
    t.print(rt);
    return 0;
}

LG-2464 [SDOI2008]郁闷的小J

#include 
using namespace std;
#define L ch[now][0]
#define R ch[now][1]
const int N = 400010;
map <int,int> p;
struct Treap {
    int key[N],pos[N],cnt,ch[N][2],rt[N],siz[N];
    int newnode(int k) {pos[++cnt]=rand();siz[cnt]=1;key[cnt]=k;return cnt;}
    void update(int now) {siz[now]=1+siz[L]+siz[R];}
    void split(int now,int k,int &x,int &y) {
        if(!now) {x=y=0;return;}
        if(kelse {x=now;split(R,k,R,y);}
        update(now);
    }
    int merge(int a,int b) {
        if(a*b==0) return a+b;
        if(pos[a]1]=merge(ch[a][1],b);
            update(a);
            return a;
        }
        else {
            ch[b][0]=merge(a,ch[b][0]);
            update(b);
            return b;
        }
    }
    void insert(int k,int v) {
        int a,b;
        split(rt[k],v,a,b);
        rt[k]=merge(a,merge(newnode(v),b));
    }
    void del(int k,int v) {
        int a,b,c;
        split(rt[k],v,a,b);
        split(a,v-1,a,c);
        c=merge(ch[c][0],ch[c][1]);
        rt[k]=merge(merge(a,c),b);
    }
    void query(int k,int l,int r) {
        int a,b,c;
        split(rt[k],r,a,b);
        split(a,l-1,a,c);
        printf("%d\n",siz[c]);
        rt[k]=merge(merge(a,c),b);
    }
}t;
int n,m,cnt,a[N];
int read();
int main() {
    srand(time(NULL));
    n=read();m=read();
    for(int i=1;i<=n;++i) {
        a[i]=read();
        if(p[a[i]]==0) p[a[i]]=++cnt;
        a[i]=p[a[i]];
        t.insert(a[i],i);
    }
    for(int i=1;i<=m;++i) {
        char ch=getchar();
        while(ch!='Q' && ch!='C') ch=getchar();
        if(ch=='Q') {
            int l=read(),r=read(),k=read();
            k=p[k];
            t.query(k,l,r);
        }
        else {
            int pos=read(),k=read();
            t.del(a[pos],pos);
            if(p[k]==0) p[k]=++cnt;
            a[pos]=p[k];
            t.insert(a[pos],pos);
        }
    }
    return 0;
}
int read() {
    int _ans=0,_flag=1;
    char _ch=getchar();
    while((_ch != '-') && (_ch > '9' || _ch < '0')) _ch=getchar();
    if(_ch == '-') {_flag = -1;_ch = getchar();}
    while(_ch >= '0' && _ch <= '9') {_ans=_ans*10+_ch-'0';_ch=getchar();}
    return _ans*_flag;
}

你可能感兴趣的:(【数据结构】Treap)