Luogu 3369 / BZOJ 3224 - 普通平衡樹 - [無旋Treap]
題目連結:
https://www.lydsy.com/JudgeOnline/problem.php?id=3224
https://www.luogu.org/problemnew/show/P3369
Description
您需要寫一種資料結構(可參考題目標題),來維護一些數,其中需要提供以下操作:
1. 插入x數
2. 刪除x數(若有多個相同的數,因只刪除一個)
3. 查詢x數的排名(若有多個相同的數,因輸出最小的排名)
4. 查詢排名為x的數
5. 求x的前驅(前驅定義為小於x,且最大的數)
6. 求x的後繼(後繼定義為大於x,且最小的數)
Input
第一行為n,表示操作的個數,下面n行每行有兩個數opt和x,opt表示操作的序號(1<=opt<=6)
Output
對於操作3,4,5,6每行輸出一個數,表示對應答案
Sample Input
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
HINT
1.n的資料範圍:n<=100000
2.每個數的資料範圍:[-2e9,2e9]
可能最近搞平衡二叉搜尋樹上癮了?搞完替罪羊樹再來搞搞無旋Treap。
關於無旋Treap:
我們已經知道了Treap是怎麼寫的:
我們知道,普通的Treap是要zigzag的,而無旋Treap顧名思義就是不需要zigzag。
無旋Treap最基本的(也是核心的)操作只有兩種,一種 $Merge$,一種 $Split$。
$Split(x,k,a,b)$ 拆分操作:按照一個判定值 $k$ 將一個Treap $x$ 拆成兩個Treap $a,b$(左樹和右樹),滿足左樹的所有值均小於等於 $k$,右樹的所有值都大於 $k$。
$Merge(x,a,b)$ 合併操作:將兩個Treap $a,b$ (滿足 $a$ 中所有元素均小於 $b$ 中所有元素),合併成一個Treap $x$。合併的原則是節點的堆權值滿足堆性質,所以是一種平衡樹。
另外,與普通Treap不同的是,每個節點均只存一個元素;也就是說,若存在若干個相同元素,會有若干個節點,而非一個節點用 $cnt$ 去記錄。
AC程式碼:
#include<bits/stdc++.h> using namespace std; const int INF=INT_MAX; const int maxn=1e5+10; /******************************** FHQ Treap - st ********************************/ int root,nodecnt; int ch[maxn][2]; int key[maxn],dat[maxn]; int siz[maxn]; int NewNode(int val) { int x=++nodecnt; key[x]=val, dat[x]=rand(); siz[x]=1, ch[x][0]=ch[x][1]=0; return x; } void Pushup(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1; } void Init() { root=nodecnt=0; key[0]=dat[0]=0; siz[0]=0, ch[0][0]=ch[0][1]=0; } void Split(int x,int k,int &a,int &b) { if(x==0) { a=b=0; return; } if(key[x]<=k) a=x, Split(ch[x][1],k,ch[a][1],b); else b=x, Split(ch[x][0],k,a,ch[b][0]); Pushup(x); } void Merge(int &x,int a,int b) { if(a==0 || b==0) { x=a+b; return; } if(dat[a]<dat[b]) x=a, Merge(ch[x][1],ch[a][1],b); else x=b, Merge(ch[x][0],a,ch[b][0]); Pushup(x); } int GetRank(int val) { int a=0,b=0; Split(root,val-1,a,b); int res=siz[a]+1; Merge(root,a,b); return res; } int GetKth(int x,int k) { if(x==0) return INF; if(siz[ch[x][0]]+1==k) return key[x]; if(siz[ch[x][0]]>=k) return GetKth(ch[x][0],k); else return GetKth(ch[x][1],k-siz[ch[x][0]]-1); } void Insert(int val) { int a=0,b=0; Split(root,val,a,b); Merge(a,a,NewNode(val)); Merge(root,a,b); } void Remove(int val) { int a=0,b=0,c=0; Split(root,val,a,b); Split(a,val-1,a,c); Merge(c,ch[c][0],ch[c][1]); Merge(a,a,c); Merge(root,a,b); } int GetPre(int val) { int a=0,b=0; Split(root,val-1,a,b); int res=GetKth(a,siz[a]); Merge(root,a,b); return res; } int GetNxt(int val) { int a=0,b=0; Split(root,val,a,b); int res=GetKth(b,1); Merge(root,a,b); return res; } /******************************** FHQ Treap - ed ********************************/ int main() { int n,opt,x; scanf("%d",&n); Init(); while(n--) { scanf("%d%d",&opt,&x); if(opt==1) Insert(x); if(opt==2) Remove(x); if(opt==3) printf("%d\n",GetRank(x)); if(opt==4) printf("%d\n",GetKth(root,x)); if(opt==5) printf("%d\n",GetPre(x)); if(opt==6) printf("%d\n",GetNxt(x)); } }