1. 程式人生 > >無旋Treap 從狂轉到不轉

無旋Treap 從狂轉到不轉

在學習平衡樹部分時,旁邊的某位C姓dalao對Treap情有獨鍾,而我卻為Splay的優美而深深著迷.這導致了對Treap的不屑一顧

這東西有什麼用,那麼多操作都不資瓷,low

如今繁華落盡,每次遇到需要使用平衡樹的題時,不禁流下了悔恨的淚(汗)水

Splay真xx**難打!

所以,今天,就讓我們來講講無旋Treap

無旋Treap

無旋Treap最大的優勢就是編碼簡單,支援的操作多,相應的,它比Splay要稍稍慢一點

無旋Treap的主要操作有兩個,splitmerge,能分裂or合併兩棵Treap.通過這兩個操作,就可以拓展出大量的其他操作

分裂

Treap的split支援在某棵Treap中分裂出大小為num的左子樹,或者是分裂出權值小於k的子樹
由於Treap具有二叉搜尋樹的性質,按sizeorkey遞迴分裂即可

同時通過物件維護分裂後兩顆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(k<key[now]) {y=now;split(L,k,x,L);}
        else {x=now;split(R,k,R,y);}
        update(now);
    }
}

合併

Treap的合併操作將兩棵Treap以一定的次序組合起來
其最終形態由兩方面決定:相對位置pos(我通常將其叫做自平衡權)
顯然,根據Treap的原理,通常pos較小的放在靠上的位置,同時treap又是二叉搜尋樹,那麼其相對位置決定了兩者之間的父子關係(屬於左子樹or右子樹)
類似與左偏樹一樣,遞迴合併即可

int merge(int a,int b) {
    if(a*b==0) return a+b;
    if(pos[a]<pos[b]) {
        ch[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的節點放在左子樹,依次合併

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的子樹合併左右兒子節點,這樣就保證了僅僅刪除一個節點.若要全部刪除,直接合並左右部分

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 <bits/stdc++.h>
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]<pos[b]) {
            ch[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 <bits/stdc++.h>
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(k<key[now]) {y=now;split(L,k,x,L);}
        else {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]<pos[b]) {
            ch[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;
}