首先說一下,node
這個東西能夠搞一切bst,treap,splay所能搞的東西ios
今天心血來潮,數組
想搞一搞平衡樹,數據結構
先百度了一下平衡樹,發現正宗的平衡樹寫法應該是在二叉查找樹的基礎上加什麼左左左右右左右右的旋轉之類的,函數
思路比較好理解,可是spa
代碼量。。。。。。。。code
一看就頭大,,blog
而後,在洛谷翻題解的時候無心間看到了遠航之曲發的一篇很是短小精悍的題解,get
因而就學了一下博客
這個東西的學名應該是叫作fhq treap,應該是treap的強化版。
整個數據結構中只有兩個操做:
1.分離(split) 就是把一棵樹分紅兩個樹
2.合併(merge)把兩棵樹合成一棵樹
對於FHQ 的兩種操做的原理以及實現,
我在這裏就不去贅述,
你們能夠去看一下遠航之曲寫的博客
http://www.yhzq-blog.cc/fhq-treap%e6%80%bb%e7%bb%93/
在這裏我主要是在講解一下代碼的具體實現(固然也有可能不對。。)
題目連接:
https://www.luogu.org/problem/show?pid=3369
先說一下各個數組的含義:
1 int ch[MAXN][3];// 0左孩子 1右孩子 2 int val[MAXN];// 每個點的權值 3 int pri[MAXN];// 隨機生成的附件權值 4 int siz[MAXN];// 以i爲節點的樹的節點數量 5 int sz;// 總結點的數量
而後來分別說明一下六中操做的實現
1.插入:
split(root,a,x,y);
root=merge(merge(x,new_node(a)),y);
這個比較好理解,咱們先把樹分爲x,y兩部分,而後把新的節點a看作是一棵樹,先與x合併,合併完以後將合併的總體與y合併
2.刪除
1 split(root,a,x,z); 2 split(x,a-1,x,y); 3 y=merge(ch[y][0],ch[y][1]); 4 root=merge(merge(x,y),z);
首先咱們把樹分爲x和z兩部分
那麼x樹中的最大權值爲a
再把x分爲x和y兩部分。
此時x中的最大權值爲a-1,且權值爲a的節點必定是y的根節點。
而後咱們能夠無視y的根節點,直接把y的左右孩子合併起來,這樣就成功的刪除了根節點,
最後再把x,y,z合併起來就好
3.查詢a的排名
1 split(root,a-1,x,y); 2 printf("%d\n",siz[x]+1); 3 root=merge(x,y);
咱們首先按照a-1的權值把樹分開。
那麼x樹中最大的應該是a-1。
那麼a的排名就是siz[x]+1
4.查詢排名爲a的數
1 printf("%d\n",val[kth(root,a)]);
直接調用查找排名的函數便可,
這個函數應該比較好理解。。
5.求x的前驅(前驅定義爲小於a,且最大的數)
1 split(root,a-1,x,y); 2 printf("%d\n",val[kth(x,siz[x])]); 3 root=merge(x,y);
由於要小於a,那麼咱們按照a-1的權值劃分,
x中最大的必定是<=a-1的,
因此咱們直接輸出x中最大的數就好,
(這裏有一個小技巧,由於siz儲存的是節點的數目,而後根據二叉查找樹的性質,編號最大的就是值最大的)
6.求x的後繼(後繼定義爲大於x,且最小的數)
1 split(root,a,x,y); 2 printf("%d\n",val[kth(y,1)]); 3 root=merge(x,y);
和上面的原理相似,
留給你們思考,
不懂的再問我。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstdlib> 7 #include<ctime> 8 using namespace std; 9 const int MAXN=100001; 10 static void read(int &n) 11 { 12 char c='+';int x=0;bool flag=0; 13 while(c<'0'||c>'9'){c=getchar();if(c=='-')flag=1;} 14 while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c-48);c=getchar();} 15 flag==1?n=-x:n=x; 16 } 17 int ch[MAXN][3];// 0左孩子 1右孩子 18 int val[MAXN];// 每個點的權值 19 int pri[MAXN];// 隨機生成的附件權值 20 int siz[MAXN];// 以i爲節點的樹的節點數量 21 int sz;// 總結點的數量 22 void update(int x) 23 { 24 siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]]; 25 } 26 int new_node(int v) 27 { 28 siz[++sz]=1;// 新開闢一個節點 29 val[sz]=v; 30 pri[sz]=rand(); 31 return sz; 32 } 33 int merge(int x,int y)// 合併 34 { 35 if(!x||!y) return x+y;// x和y中一定有一個是0 36 if(pri[x]<pri[y])// 把x加到左邊的樹上 37 { 38 ch[x][1]=merge(ch[x][1],y);// 不懂的看GIF圖 39 update(x); 40 return x; 41 } 42 else 43 { 44 ch[y][0]=merge(x,ch[y][0]); 45 update(y); 46 return y; 47 } 48 } 49 void split(int now,int k,int &x,int &y) 50 { 51 if(!now) x=y=0;// 到達葉子節點 52 else 53 { 54 if(val[now]<=k)// 分離右子樹 55 x=now,split(ch[now][1],k,ch[now][1],y); 56 else 57 y=now,split(ch[now][0],k,x,ch[now][0]); 58 update(now); 59 } 60 } 61 int kth(int now,int k)// 查詢排名 62 { 63 while(1) 64 { 65 if(k<=siz[ch[now][0]]) 66 now=ch[now][0];// 在左子樹中,且數量小於左子樹的大小,迭代尋找 67 else if(k==siz[ch[now][0]]+1) 68 return now;// 找到了 69 else 70 k-=siz[ch[now][0]]+1,now=ch[now][1];// 去右子樹找 71 } 72 } 73 int main() 74 { 75 srand((unsigned)time(NULL)); 76 int n; 77 read(n); 78 int root=0,x,y,z; 79 for(int i=1;i<=n;i++) 80 { 81 int how,a; 82 read(how);read(a); 83 if(how==1)// 插入 84 { 85 split(root,a,x,y); 86 root=merge(merge(x,new_node(a)),y); 87 } 88 else if(how==2)//刪除x 89 { 90 split(root,a,x,z); 91 split(x,a-1,x,y); 92 y=merge(ch[y][0],ch[y][1]); 93 root=merge(merge(x,y),z); 94 } 95 else if(how==3)//查詢x的排名 96 { 97 split(root,a-1,x,y); 98 printf("%d\n",siz[x]+1); 99 root=merge(x,y); 100 } 101 else if(how==4)// 查詢排名爲x的數 102 { 103 printf("%d\n",val[kth(root,a)]); 104 } 105 else if(how==5)// 求x的前驅 106 { 107 split(root,a-1,x,y); 108 printf("%d\n",val[kth(x,siz[x])]); 109 root=merge(x,y); 110 } 111 else if(how==6)// 求x的後繼 112 { 113 split(root,a,x,y); 114 printf("%d\n",val[kth(y,1)]); 115 root=merge(x,y); 116 } 117 } 118 return 0; 119 }
最後說一下,FHQ實際上是能夠處理區間問題的,
主要就是先把r+1的拆出來,而後把l的拆出來。
可是有些細節問題特別神奇,至今沒有搞懂。
若是你會的話但願你能給本蒟蒻講一下。
謝謝