「FHQ Treap」學習筆記
阿新 • • 發佈:2018-11-24
如果 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, intk, 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」學習筆記