本文內容部分轉自某大佬博客:https://blog.csdn.net/CABI_ZGX/article/details/79963427
例題:https://www.luogu.org/problemnew/show/P3369#sub
題目描述
您需要寫一種數據結構(可參考題目標題),來維護一些數,其中需要提供以下操作:
- 插入 x 數
- 刪除 x 數(若有多個相同的數,因只刪除一個)
- 查詢 x 數的排名(排名定義為比當前數小的數的個數 +1 。若有多個相同的數,因輸出最小的排名)
- 查詢排名為 x 的數
- 求 x 的前驅(前驅定義為小於 x ,且最大的數)
- 求 x 的后繼(后繼定義為大於 x ,且最小的數)
輸入輸出格式
輸入格式:
第一行為 n ,表示操作的個數,下面 n 行每行有兩個數 opt 和 x ,opt 表示操作的序號( 1≤opt≤6 )
輸出格式:
對於操作 3,4,5,6 每行輸出一個數,表示對應答案
看題目就知道Treap當然是可以的,考慮fhq-treap
fhq-treap的最大特點,不需要不需要不需要旋轉!!!這是棵不考旋轉保持平衡的平衡樹
不管怎么樣,我們要讓它是平衡的。好的其實有些除旋轉之外的神奇的操作是有效的,下面來講一講。
對於隨機數我們維護小根堆;對於權值左兒子的權值小於當前節點的權值,右兒子的權值大於當前節點的權值
Mergy
merge就是把兩個treap合成一個,是一個遞歸的過程。首先明確fhq-treap的平衡條件是他的附加權值,那么只需要比較他們的附加權值大小判斷在左樹還是右樹即可。
注意x樹里的所有點的權值小於y樹里的所有點的權值
inline int mergy(int x,int y) { if (!x||!y) return x|y; if (a[x].rnd<a[y].rnd) { a[x].son[1]=mergy(a[x].son[1],y); update(x); return x; } else { a[y].son[0]=mergy(x,a[y].son[0]); update(y); return y; } }
Split
split是把一顆treap分開兩個樹的操作,也是一個遞歸的過程。有兩種分法,一種是按權值分,一種是按size(子樹大小)分。
按權值分(注意這時候權值小於等於 k的節點都在左樹中,大於k的都在右樹中)
void split(int now,int k,int &x,int &y) { if (!now) { x=y=0; return; } if (k>=a[now].dat) { x=now; split(a[now].son[1],k,a[now].son[1],y); } else { y=now; split(a[now].son[0],k,x,a[now].son[0]); } update(now); return; }
按size分
void split(int now,int k,int &x,int &y) { if(!now) x=y=0; else { update(now); if (k<=tr[tr[now].son[0]].size) y=now,split(tr[now].son[0],k,x,tr[now].son[0]); else x=now,split(tr[now].son[1],k-tr[tr[now].son[0]].size-1,tr[now].son[1],y); update(now); } }
New,建立新的節點
int New(int x) { tot++; a[tot].dat=x;a[tot].rnd=rand();a[tot].size=1; return tot; }
找kth的節點的權值
int kth(int now,int k) { while (1) { if (k<=a[a[now].son[0]].size) now=a[now].son[0]; else { if (k==a[a[now].son[0]].size+1) return now; else { k=k-(a[a[now].son[0]].size+1); now=a[now].son[1]; } } } }
Insert
插入一個權值為t節點,那么只需要以t為權值split整棵樹,然后New(t)在merge回去
split(root,t,x,y); root=mergy(mergy(x,New(t)),y);
Del
刪除權值為t的點,先把整顆樹以t為權值split成兩棵樹x,z,再把x樹按照t-1分成x,y。這時候值為t的點一定為y的根,那么我們把y的兩個子兒子merge起來,再把他們重新merge起來得到一個新的樹,這顆樹就去除掉了t。
split(root,t,x,z); split(x,t-1,x,y); y=mergy(a[y].son[0],a[y].son[1]); root=mergy(mergy(x,y),z);
FindRank
找值為t的節點排名,就把整棵樹以t-1為權值split,那么小於t的節點全在左樹內,輸出左樹的size即可
precursor
找前驅就把整棵樹按t-1為權值split開,此時小於等於t的數全在左樹里,在左樹里找到排名為(左樹的size)的節點權值即可。
split(root,t-1,x,y); printf("%d\n",a[kth(x,a[x].size)].dat); root=mergy(x,y);
successor
找后繼是相同的,把整棵樹按t為權值split開,此時右樹排名第一的數就是后繼
split(root,t,x,y); printf("%d\n",a[kth(y,1)].dat); root=mergy(x,y);
fhq-treap代碼:
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<time.h>
using namespace std; const int maxn=1e5+15; int n,tot,root; struct fhq { int son[2]; int rnd,dat,size; }a[maxn]; inline int read() { char ch=getchar(); int s=0,f=1; while (!(ch>='0'&&ch<='9')) {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } inline void update(int x) { a[x].size=a[a[x].son[0]].size+a[a[x].son[1]].size+1; return; } int New(int x) { tot++; a[tot].dat=x;a[tot].rnd=rand();a[tot].size=1; return tot; } inline int mergy(int x,int y) { if (!x||!y) return x|y; if (a[x].rnd<a[y].rnd) { a[x].son[1]=mergy(a[x].son[1],y); update(x); return x; } else { a[y].son[0]=mergy(x,a[y].son[0]); update(y); return y; } } void split(int now,int k,int &x,int &y) { if (!now) { x=y=0; return; } if (k>=a[now].dat) { x=now; split(a[now].son[1],k,a[now].son[1],y); } else { y=now; split(a[now].son[0],k,x,a[now].son[0]); } update(now); return; } int kth(int now,int k) { while (1) { if (k<=a[a[now].son[0]].size) now=a[now].son[0]; else { if (k==a[a[now].son[0]].size+1) return now; else { k=k-(a[a[now].son[0]].size+1); now=a[now].son[1]; } } } } int main() { srand(time(0)); n=read(); while (n--) { int opt=read(),t=read(); int x,y,z; if (opt==1) { split(root,t,x,y); root=mergy(mergy(x,New(t)),y); } if (opt==2) { split(root,t,x,z); split(x,t-1,x,y); y=mergy(a[y].son[0],a[y].son[1]); root=mergy(mergy(x,y),z); } if (opt==3) { split(root,t-1,x,y); printf("%d\n",a[x].size+1); root=mergy(x,y); } if (opt==4) { int k=kth(root,t); printf("%d\n",a[k].dat); } if (opt==5) { split(root,t-1,x,y); printf("%d\n",a[kth(x,a[x].size)].dat); root=mergy(x,y); } if (opt==6) { split(root,t,x,y); printf("%d\n",a[kth(y,1)].dat); root=mergy(x,y); } } return 0; }
Treap代碼:
#include<bits/stdc++.h>
using namespace std; const int maxn=1e5+15; const int inf=1e9+7; int n,m,tot,root; struct TREAP { int l,r; int val,dat; int cnt,size; }a[maxn]; inline void read(int &x) { char ch=getchar(); int s=0,f=1; while (!(ch>='0'&&ch<='9')) {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} x=s*f; } int New(int val){ a[++tot].val=val; a[tot].cnt=a[tot].size=1; a[tot].dat=rand(); return tot; } void update(int p){ a[p].size=a[a[p].l].size+a[p].cnt+a[a[p].r].size; } void build() { New(-inf);New(inf); root=1;a[1].r=2; update(root); } int get_rank_by_val(int p,int val) { if (!p) return 0; if (val==a[p].val) return a[a[p].l].size+1; if (val<a[p].val) return get_rank_by_val(a[p].l,val); return a[a[p].l].size+a[p].cnt+get_rank_by_val(a[p].r,val); } int get_val_by_rank(int p,int rank) { if (!p) return inf; if (a[a[p].l].size>=rank) return get_val_by_rank(a[p].l,rank); if (a[a[p].l].size+a[p].cnt>=rank) return a[p].val; return get_val_by_rank(a[p].r,rank-a[a[p].l].size-a[p].cnt); } void zig(int &p) { int q=a[p].l; a[p].l=a[q].r;a[q].r=p;p=q; update(a[p].r);update(p); } void zag(int &p) { int q=a[p].r; a[p].r=a[q].l;a[q].l=p;p=q; update(a[p].l);update(p); } void insert(int &p,int val) { if (!p){ p=New(val); return; } if (val==a[p].val){ a[p].cnt++;update(p); return; } if (val<a[p].val){ insert(a[p].l,val); if (a[p].dat<a[a[p].l].dat) zig(p); } else { insert(a[p].r,val); if (a[p].dat<a[a[p].r].dat) zag(p); } update(p); } int getpre(int val) { int ans=1; int p=root; while (p){ if (val==a[p].val){ if (a[p].l>0) { p=a[p].l; while (a[p].r>0) p=a[p].r; ans=p; } break; } if (a[p].val<val&&a[p].val>a[ans].val) ans=p; if (val<a[p].val) p=a[p].l;else p=a[p].r; } return a[ans].val; } int getnext(int val) { int ans=2; int p=root; while (p){ if (val==a[p].val){ if (a[p].r>0){ p=a[p].r; while (a[p].l>0) p=a[p].l; ans=p; } break; } if (a[p].val>val&&a[p].val<a[ans].val) ans=p; if (val<a[p].val) p=a[p].l;else p=a[p].r; } return a[ans].val; } void remove(int &p,int val) { if (!p) return; if (val==a[p].val){ if (a[p].cnt>1) { a[p].cnt--;update(p); return; } if (a[p].l||a[p].r){ if (a[p].r==0||a[a[p].l].dat>a[a[p].r].dat){ zig(p);remove(a[p].r,val); } else { zag(p);remove(a[p].l,val); } update(p); } else p=0; return; } if (val<a[p].val) remove(a[p].l,val);else remove(a[p].r,val); update(p); } int main() { int opt; build(); read(n); while (n--) { read(opt);int x; read(x); if (opt==1) insert(root,x); if (opt==2) remove(root,x); if (opt==3) printf("%d\n",get_rank_by_val(root,x)-1); if (opt==4) printf("%d\n",get_val_by_rank(root,x+1)); if (opt==5) printf("%d\n",getpre(x)); if (opt==6) printf("%d\n",getnext(x)); } return 0; }
其實還有vector寫法:
#include<bits/stdc++.h>
using namespace std; int n,opt,x; vector <int> p; int main() { p.reserve(100001+15); scanf("%d",&n); while (n--) { scanf("%d%d",&opt,&x); if (opt==1) p.insert(lower_bound(p.begin(),p.end(),x),x); if (opt==2) p.erase(lower_bound(p.begin(),p.end(),x)); if (opt==3) printf("%d\n",lower_bound(p.begin(),p.end(),x)-p.begin()+1); if (opt==4) printf("%d\n",p[x-1]); if (opt==5)printf("%d\n",p[lower_bound(p.begin(),p.end(),x)-p.begin()-1]); if (opt==6) printf("%d\n",p[upper_bound(p.begin(),p.end(),x)-p.begin()]); } return 0; }