可持久化Treap(fhq Treap,非旋轉式Treap)學習(未完待續)
1: split
將Treap按照權值或排名分裂為兩棵Treap 我只寫了按權值分裂
對於我們遍歷到每一個點,假如它的權值小於k,那麽它的所有左子樹,都要分到左邊的樹裏,然後遍歷它的右兒子。假如大於k,把它的所有右子樹分到右邊的樹裏,遍歷左兒子。
因為它的最多操作次數就是一直分到底,效率就是O(logn)。
void split(int now,int k,int &x,int &y) { if(!now) x=y=0; else { if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y); else y=now,split(ch[now][0],k,x,ch[now][0]); update(now); } }
2: merge
這個就是把兩個Treap合成一個,保證第一個的權值小於第二個。
因為第一個Treap的權值都比較小,我們比較一下它的fix(附加權值),假如第一個的fix小,我們就可以直接保留它的所有左子樹,接著把第一個Treap變成它的右兒子。反之,我們可以保留第二棵的所有右子樹,指針指向左兒子。
你可以把這個過程形象的理解為在第一個Treap的左子樹上插入第二個樹,也可以理解為在第二個樹的左子樹上插入第一棵樹。因為第一棵樹都滿足小於第二個樹,所以就變成了比較fix來確定樹的形態。
也就是說,我們其實是遍歷了第一個trep的根->最大節點,第二個Treap的根->最小節點,也就是O(logn)
int merge(int A,int B) { if(!A||!B) return A+B; if(fix[A]<fix[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;} }
下面我們就可以通過這兩個基本的東西實現各種各樣的操作了。
3:insertinsert
插入一個權值為v的點,把樹按照v的權值split成兩個,再按照順序merge回去。
4: deldel
刪除權值為v的點,把樹按照v分成兩個a,b,再把a按照v-1分成c,d。把c的兩個子兒子merge起來,再merge(merge(c,d),b)
(因為把c的兩個兒子merge起來之後,如果權值為v的節點有一個,就已經把他刪除了,因為merge後c=0;若有多個就刪除了一個)
5: precursorprecursor
找前驅的話把root按v-1 split成x,y,在x裏面找最大值
6: successorsuccessor
找後繼的話把root按v split成x,y,在y裏找最小值
7: rankrank
把root按v-1 split成x,y,排名是x的siz
代碼:https://www.luogu.org/problem/show?pid=3369 普通平衡樹
#include<iostream> #include<cstdio> #include<cstring> #include<ctime> #include<cstdlib> #define maxn 500001 using namespace std; int size[maxn],ch[maxn][2],fix[maxn],val[maxn]; int T,cnt,n,m,x,y,z,p,a,root,com; inline int read() { int x=0,f=1;char c=getchar(); while(c>‘9‘||c<‘0‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();} return x*f; } inline void update(int x) { size[x]=1+size[ch[x][0]]+size[ch[x][1]]; } inline int new_node(int x) { size[++cnt]=1; val[cnt]=x; fix[cnt]=rand(); return cnt; } int merge(int A,int B) { if(!A||!B) return A+B; if(fix[A]<fix[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 k,int &x,int &y) { if(!now) x=y=0; else { if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y); else y=now,split(ch[now][0],k,x,ch[now][0]); update(now); } } int kth(int now,int k) { while(1) { if(k<=size[ch[now][0]]) now=ch[now][0]; else if(k==size[ch[now][0]]+1) return now; else k-=size[ch[now][0]]+1,now=ch[now][1]; } } int main() { srand((unsigned)time(NULL)); T=read(); while(T--) { p=read();a=read(); if(p==1) { split(root,a,x,y); root=merge(merge(x,new_node(a)),y); } else if(p==2) { split(root,a,x,z); split(x,a-1,x,y); y=merge(ch[y][0],ch[y][1]); root=merge(merge(x,y),z); } else if(p==3) { split(root,a-1,x,y); printf("%d\n",size[x]+1); root=merge(x,y); } else if(p==4) printf("%d\n",val[kth(root,a)]); else if(p==5) { split(root,a-1,x,y); printf("%d\n",val[kth(x,size[x])]); root=merge(x,y); } else { split(root,a,x,y); printf("%d\n",val[kth(y,1)]); root=merge(x,y); } } return 0; }
可持久化Treap(fhq Treap,非旋轉式Treap)學習(未完待續)