1. 程式人生 > >「FHQ Treap」學習筆記

「FHQ Treap」學習筆記

如果 font nod turn for spa 本質 kth 最大的

話說天下大事,就像fhq treap —— 分久必合,合久必分


簡單講一講。非旋treap主要依靠分裂和合並來實現操作。(遞歸,不維護fa)

合並的前提是兩棵樹的權值滿足一邊的最大的比另一邊最小的還小。因此時合並時只需要維護鍵值的堆性質即可。這樣每一次比較根節點,如果x比y小那麽y直接接到x的右子樹即可(需要滿足權值的平衡樹性質);否則的話只需要反過來,把x接到y的左子樹上。merge函數返回的值應當是合並完後的根節點。

分裂分為兩種,排名和權值。然而我認為它們本質上是一樣的。對於權值的分裂,對於每一個子樹的根節點,若根節點比給定值a小,那麽此節點及左子樹一定比a小,歸入x。否則此節點及右子樹歸入y。然後再遞歸操作還沒有分類的那一個子樹就好了。代碼實現中的引用用的非常巧妙。可以把這裏的引用理解為是需要修改的東西,利用遞歸的過程對其作出修改。

合並看鍵值,分裂看權值。


$Merge$

int merge(int u, int v){
    if(!u||!v) return u|v;
    if(key[u] < key[v]){
        ch[u][1] = merge(ch[u][1], v);
        pushup(u); 
        return u;
    }
    else{
        ch[v][0] = merge(u, ch[v][0]);
        pushup(v); 
        return v;
    }
}

$Split$

void split_a(int u, int a, int& x, int& y){
    if(!u){ x = y = 0; return; }
    if(val[u] <= a){
        x = u;
        split_a(ch[u][1], a, ch[u][1], y);
    }
    else{
        y = u;
        split_a(ch[u][0], a, x, ch[u][0]);
    }
    pushup(u);
}
void split_k(int u, int
k, int& x, int& y){ if(!u){ x = y = 0; return; } if(size[ch[u][0]]+cnt[u] <= k){ x = u; split_k(ch[u][1], k-size[ch[u][0]]-cnt[u], ch[u][1], y); } else{ y = u; split_k(ch[u][0], k, x, ch[u][0]); } pushup(u); }

有了這兩個操作,其他操作yy一下即可,非常簡便。

當然,在查詢k大排名或前驅後繼時,完全可以用普通平衡樹(如splay)的做法。因為它滿足平衡樹的性質。也就是傻乎乎的去logn的從根節點往下走。那麽既然我們有了split,查詢最大最小值應該很方便了。要盡量讓代碼精簡啊!

My Code:

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int MAXN = 100010;
const int INF = 0x3f3f3f3f;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
    int x = 0; int w = 1; register char c = getchar();
    for(; c ^ - && (c < 0 || c > 9); c = getchar());
    if(c == -) w = -1, c = getchar();
    for(; c >= 0 && c <= 9; c = getchar()) x = (x<<3) + (x<<1) + c - 0; return x * w;
}
int N,opt,x,num_node,RooT;
int ch[MAXN][2],val[MAXN],key[MAXN],size[MAXN],cnt[MAXN];
inline void pushup(int x){
    size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x];
}
inline void clear(int x){
    size[x]=cnt[x]=val[x]=key[x]=ch[x][0]=ch[x][1]=0;
}
int merge(int u, int v){
    if(!u||!v) return u|v;
    if(key[u] < key[v]){
        ch[u][1] = merge(ch[u][1], v);
        pushup(u); 
        return u;
    }
    else{
        ch[v][0] = merge(u, ch[v][0]);
        pushup(v); 
        return v;
    }
}
void split_a(int u, int a, int& x, int& y){
    if(!u){ x = y = 0; return; }
    if(val[u] <= a){
        x = u;
        split_a(ch[u][1], a, ch[u][1], y);
    }
    else{
        y = u;
        split_a(ch[u][0], a, x, ch[u][0]);
    }
    pushup(u);
}
void split_k(int u, int k, int& x, int& y){
    if(!u){ x = y = 0; return; }
    if(size[ch[u][0]]+cnt[u] <= k){
        x = u;
        split_k(ch[u][1], k-size[ch[u][0]]-cnt[u], ch[u][1], y);
    }
    else{
        y = u;
        split_k(ch[u][0], k, x, ch[u][0]);
    }
    pushup(u);
}
inline void Insert(int v){//以v進行分裂,插入後合並。 
    val[++num_node]= v;
    key[num_node] = rand();
    size[num_node] = cnt[num_node] = 1;
    int a,b;
    split_a(RooT, v, a, b);
    RooT = merge(merge(a,num_node), b);
}
inline void Delete(int v){//以v進行分裂,刪除後合並。 
    int a,b,c,d;
    split_a(RooT, v, a, b);
    split_k(a, size[a]-1, c, d);
    if(cnt[d] > 1) --cnt[d];
    else clear(d);
    RooT = merge(c, b);
}
inline int Rank(int v){//以v-1進行分裂,看左側樹的size 
    int a,b,ans;
    split_a(RooT, v-1, a, b);
    ans = size[a]+1;
    RooT = merge(a, b);
    return ans;
}
inline int Kth(int k){//以k進行分裂,依舊看左側的size,但註意再分裂一次取出比k小的 
    int a,b,c,d,ans;
    split_k(RooT, k, a, b);
    split_k(a,size[a]-1,c,d);
    ans = val[d];
    RooT = merge(merge(c,d),b);
    return ans;
}
inline int Pre(int v){//與kth同理 
    int a,b,c,d,ans;
    split_a(RooT, v-1, a, b);
    split_k(a,size[a]-1,c,d);
    ans = val[d];
    RooT = merge(merge(c,d),b);
    return ans;
}
inline int Nxt(int v){
    int a,b,c,d,ans;
    split_a(RooT, v, a, b);
    split_k(b, 1, c, d);
    ans = val[c];
    RooT = merge(a,merge(c,d));
    return ans;
}
void PrintTree(int u){
    if(ch[u][0]) PrintTree(ch[u][0]);
    printf("%d ",val[u]);
    if(ch[u][1]) PrintTree(ch[u][1]);
}
int main(){
//    freopen(".in","r",stdin);
    srand((unsigned)time(NULL));
    N = read();
    while(N--){
        opt = read(), x = read();
        if(opt == 1) Insert(x);
        if(opt == 2) Delete(x);
        if(opt == 3) printf("%d\n", Rank(x));
        if(opt == 4) printf("%d\n", Kth(x));
        if(opt == 5) printf("%d\n", Pre(x));
        if(opt == 6) printf("%d\n", Nxt(x));
    }
    return 0;
}

「FHQ Treap」學習筆記