[總結] fhq_Treap 學習筆記


轉自

無旋版 $Treap$。

只需要兩個操作即可達到 $splay$ 的所有功能

1、$split$

它的主要思想就是把一個 $Treap$ 分成兩個。

 $split$ 操作有兩種類型,一種是按照權值分配,一種是按前 k 個分配。

第一種就是把所有小於 k 的權值的節點分到一棵樹中,第二種是把前 k 個分到一個樹里。

權值版:

 1 void split(int o,int k,int &x,int &y){ //這里的x,y分別是將以o為根的樹切開后第一個新子樹的根和第二個新子樹的根
 2     if(!o) x=y=0;
 3     else {
 4         if(val[o]<=k)
 5             x=o,split(ch[o][1],k,ch[o][1],y);
 6         else 
 7             y=o,split(ch[o][0],k,x,ch[o][0]);
 8         pushup(o);
 9     }
10 }

對於我們遍歷到每一個點,假如它的權值小於k,那么它的所有左子樹,都要分到左邊的樹里,然后遍歷它的右兒子。假如大於k,把它的所有右子樹分到右邊的樹里,遍歷左兒子。

因為它的最多操作次數就是一直分到底,效率就是 $O(logn)$。

 

對於前k個版的,就是像找第k大的感覺。每次減掉sze

void split(int now,int k,int &x,int &y){
    if (!now) x=y=0;
    else{
        if (k<=siz[ch[now][0]])
            y=now,split(ch[now][0],k,x,ch[now][0]);
        else
            x=now,split(ch[now][1],k-sze[ch[now][0]]-1,ch[now][1],y);
        pushup(now);
    }
}

2、$merge$

這個就是把兩個 $Treap$ 合成一個,保證第一個的權值小於第二個。

因為第一個 $Treap$ 的權值都比較小,我們比較一下它的 $prio$ (優先級),假如第一個的 $prio$ 小,我們就可以直接保留它的所有左子樹,接着把第一個 $Treap$ 變成它的右兒子。反之,我們可以保留第二棵的所有右子樹,指針指向左兒子。

你可以把這個過程形象的理解為在第一個 $ Treap$ 的右子樹上插入第二個樹,也可以理解為在第二個樹的左子樹上插入第一棵樹。因為第一棵樹都滿足小於第二個樹,所以就變成了比較 $prio$ 來確定樹的形態。

也就是說,我們其實是遍歷了第一個$Treap$ 的根->最大節點,第二個$Treap$的根->最小節點,也就是 $O(logn)$

int merge(int x,int y){
    if(!x or !y) return x+y;
    if(prio[x]<prio[y]){
        ch[x][1]=merge(ch[x][1],y);
        pushup(x);
        return x;
    }
    else{
        ch[y][0]=merge(x,ch[y][0]);
        pushup(y);
        return y;
    }
}

下面我們就可以通過這兩個基本的東西實現各種各樣的操作了。

3、insert

插入一個權值為 $k$ 的點,把樹按照 $k$ 的權值 $split$ 成兩個,再 $merge$ 回去。

4、remove

刪除權值為 $k$ 的點,把樹按照 $k$ 分成兩個$a,b$ 再把$a$ 按照 $k-1$ 分成$c,d$。把$d$ 的兩個兒子 $merge$起來,再 $merge(merge(c,d),b)$

void remove(int k){
    int x,y,z;
    split(Root,k,x,y);
    split(x,k-1,x,z);
    z=merge(ch[z][0],ch[z][1]);
    Root=merge(x,merge(z,y));
}

其它見代碼

// 普通平衡樹 fhq_Treap
// By YoungNeal
#include<cstdio>
#include<cstdlib>
#define N 100005
#define inf 0x3f3f3f3f

int Root;
int n,opt,x,tot;
int val[N],prio[N];
int sze[N],ch[N][2];

void pushup(int o){
    sze[o]=sze[ch[o][0]]+sze[ch[o][1]]+1;
}

void split(int o,int k,int &x,int &y){
    if(!o) x=y=0;
    else {
        if(val[o]<=k)
            x=o,split(ch[o][1],k,ch[o][1],y);
        else 
            y=o,split(ch[o][0],k,x,ch[o][0]);
        pushup(o);
    }
}

int merge(int x,int y){
    if(!x or !y) return x+y;
    if(prio[x]<prio[y]){
        ch[x][1]=merge(ch[x][1],y);
        pushup(x);
        return x;
    }
    else{
        ch[y][0]=merge(x,ch[y][0]);
        pushup(y);
        return y;
    }
}

int newnode(int v){
    sze[++tot]=1;
    val[tot]=v;
    prio[tot]=rand();
    return tot;
}

void insert(int k){
    int x,y;
    split(Root,k,x,y);
    Root=merge(merge(x,newnode(k)),y);
}

void remove(int k){
    int x,y,z;
    split(Root,k,x,y);
    split(x,k-1,x,z);
    z=merge(ch[z][0],ch[z][1]);
    Root=merge(x,merge(z,y));
}

void kthrank(int k){
    int x,y;
    split(Root,k-1,x,y);
    printf("%d\n",sze[x]+1);
    Root=merge(x,y);
}

int rank(int o,int k){
    if(sze[ch[o][0]]==k-1) return val[o];
    if(sze[ch[o][0]]>=k) return rank(ch[o][0],k);
    return rank(ch[o][1],k-sze[ch[o][0]]-1);
}

void prev(int k){
    int x,y;
    split(Root,k-1,x,y);
    printf("%d\n",rank(x,sze[x]));
    Root=merge(x,y);
}

void nxt(int k){
    int x,y;
    split(Root,k,x,y);
    printf("%d\n",rank(y,1));
    Root=merge(x,y);
}

signed main(){
    scanf("%d",&n);
    while(n--){
        scanf("%d%d",&opt,&x);
        if(opt==1) insert(x);
        if(opt==2) remove(x);
        if(opt==3) kthrank(x);
        if(opt==4) printf("%d\n",rank(Root,x));
        if(opt==5) prev(x);
        if(opt==6) nxt(x);
    }
    return 0;
}

5、區間操作

對於翻轉區間 $[l,r]$,我們可以先把區間 $[1,l-1]$ $split$ 出來,再把 $[l,r]$ $split$ 出來就行了。注意 $lazy$ 標記及時清除。

// 文藝平衡樹 fhp_Treap
// By YoungNeal
#include<ctime>
#include<cstdio>
#include<cstdlib>
#define N 100005

int Root;
int lazy[N];
int n,m,cnt;
int val[N],sze[N];
int ch[N][2],prio[N];

void pushup(int o){
    sze[o]=sze[ch[o][0]]+sze[ch[o][1]]+1;
}

void pushdown(int o){
    if(!lazy[o] or !o) return;
    ch[o][0]^=ch[o][1]^=ch[o][0]^=ch[o][1];
    lazy[ch[o][0]]^=1;
    lazy[ch[o][1]]^=1;
    lazy[o]=0;
}

void split(int o,int k,int &x,int &y){
    if(!o) x=y=0;
    else{
        pushdown(o);
        if(k>sze[ch[o][0]]) x=o,split(ch[o][1],k-sze[ch[o][0]]-1,ch[o][1],y);
        else y=o,split(ch[o][0],k,x,ch[o][0]);
        pushup(o);
    }
}

int merge(int x,int y){
    if(!x or !y) return x+y;
    pushdown(x); pushdown(y);
    if(prio[x]<prio[y]){
        ch[x][1]=merge(ch[x][1],y);
        pushup(x);
        return x;
    }
    else{
        ch[y][0]=merge(x,ch[y][0]);
        pushup(y);
        return y;
    }
}

int newnode(int v){
    val[++cnt]=v;
    sze[cnt]=1;
    prio[cnt]=rand();
    return cnt;
}

void res(int l,int r){
    int a,b,c,d;
    split(Root,r,a,b);
    split(a,l-1,c,d);
    lazy[d]^=1;
    Root=merge(merge(c,d),b);
}

void dfs(int now){
    if(!now) return;
    pushdown(now);
    dfs(ch[now][0]);
    printf("%d ",val[now]);
    dfs(ch[now][1]);
}

signed main(){
    srand(time(0));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        Root=merge(Root,newnode(i));
    //printf("Root=%d\n",Root);
    for(int x,y,i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        res(x,y);
        //printf("i=%d\n",i);
        //dfs(Root);
    }
    //printf("Root=%d\n",Root);
    dfs(Root);
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM