學習筆記:fhq treap
阿新 • • 發佈:2018-12-10
-1. Lead in
某一天,蒟蒻Vegetable_Duck突然想學平衡樹,百度一下,發現基本都要旋轉,這對蒟蒻Vegetable_Duck是致命地打擊QAQ.一籌莫展之際,它突然看到了一種不用旋轉的平衡樹——
0. 前置知識:的定義
樹堆,在資料結構中也稱Treap,是指有一個隨機附加域滿足堆的性質的二叉搜尋樹,其結構相當於以隨機資料插入的二叉搜尋樹。 ——摘自百度百科
形象化一點:
- 是關於的二叉搜尋樹
- 是關於的二叉搜尋樹
為了滿足上面兩條性質,就要分情況對
1.
基本定義&操作
解釋下陣列含義
int son[N][2]; //0:左兒子 1:右兒子
int sz[N];//子樹大小
int val[N];//權值
int rdm[N];//隨機權值
int cnt;//總treap的節點個數
如何開一個新節點
int new_node(int x) {
val[++cnt]=x;//節點權值
rdm[cnt]=rand();//隨機權值
sz[cnt]=1;//子樹大小
return cnt;
}
inline void pushup(int x) {
sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
}
核心操作
的核心操作只有兩種:和,這兩種操作巧妙玄學地讓每次插入和刪除,的性質都不會被破壞,自然也就不用旋轉
把a、b兩棵合成一棵大的操作 滿足a所有節點的權值 < b所有節點的權值 這樣就只用維護隨機權值的性質
程式碼:
void merge(int &now,int x,int y) { //分別為根節點地址,a,b
if(!x || !y) {
now=x+y;
return;
}
if(rdm[x]<rdm[y]) { //維護rdm堆
now=x;//保留a的左子樹
merge(son[now][1],son[now][1],y);//把b塞進a的右子樹,維護bst
}else { //原理同上
now=y;
merge(son[now][0],x,son[now][0]);
}
pushup(now);
}
把一棵大按照某種劃分標準分成、兩棵的操作
常用的劃分標準有權值和子樹大小,兩種程式碼都會給
程式碼:
- 按val劃分
//劃分後,a中所有元素<=k,b中所有元素>k
void split(int now,int k,int &x,int &y) {//分別為:當前節點,劃分標準,a的樹根,b的樹根
if(!now) {//沒東西了
x=y=0;
return;
}
if(val[now]<=k) {//利用bst的性質
x=now;
split(son[now][1],k,son[now][1],y);
}else {
y=now;
split(son[now][0],k,x,son[now][0]);
}
pushup(now);
}
- 按sz劃分
//把大treap中前k小的元素放到a中,剩下放到b中
void split(int now,int k,int &x,int &y) {
if(!now) {//沒東西了
x=y=0;
return;
}
if(sz[son[now][0]]>k) {
x=now;
split(son[now][1],k-sz[son[now][0]]-1,son[now][1],y);
}else {
y=now;
split(son[now][0],k,x,son[now][0]);
}
pushup(now);
}
2.模板題
洛谷 3369/BZOJ 3224/Tyvj 1728 普通平衡樹
#include <bits/stdc++.h>
#define N 100005
using namespace std;
int root=0;
struct fhq_treap {
int son[N][2],sz[N],val[N],rdm[N],cnt;
inline int new_node(int x) {
val[++cnt]=x,rdm[cnt]=rand(),sz[cnt]=1;
return cnt;
}
inline void pushup(int x) {
sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
}
void split(int now,int k,int &x,int &y) {
if(!now) {
x=y=0;
return;
}
if(val[now]<=k) {
x=now;
split(son[now][1],k,son[now][1],y);
}else {
y=now;
split(son[now][0],k,x,son[now][0]);
}
pushup(now);
}
void merge(int &now,int x,int y) {
if(!x || !y) {
now=x+y;
return;
}
if(rdm[x]<rdm[y]) {
now=x;
merge(son[now][1],son[now][1],y);
}else {
now=y;
merge(son[now][0],x,son[now][0]);
}
pushup(now);
}
void kth(int now,int k) {//第k大數
while(1) {
if(sz[son[now][0]]>=k) {
now=son[now][0];
}else {
if(sz[son[now][0]]+1==k) break;
k-=(sz[son[now][0]]+1);
now=son[now][1];
}
}
printf("%d\n",val[now]);
}
}T;
int main() {
int n,op,x,a,b,c;
scanf("%d",&n);
T.sz[0]=0;
T.cnt=0;
while(n--) {
a=0,b=0;
scanf("%d%d",&op,&x);
if(op==1) { //插入x數
T.split(root,x,a,b);
int t=T.new_node(x);
T.merge(a,a,t);
T.merge(root,a,b);
}
if(op==2) { //刪除x數
T.split(root,x,a,b);
T.split(a,x-1,a,c);
T.merge(c,T.son[c][0],T.son[c][1]);
T.merge(a,a,c);
T.merge(root,a,b);
}
if(op==3) { //查詢x數的排名
T.split(root,x-1,a,b);
printf("%d\n",T.sz[a]+1);
T.merge(root,a,b);
}
if(op==4) { //查詢排名為x的數
T.kth(root,x);
}
if(op==5) { //求x的前驅
T.split(root,x-1,a,b);
T.kth(a,T.sz[a]);
T.merge(root,a,b);
}
if(op==6) { //求x的後繼
T.split(root,x,a,b);
T.kth(b,1);
T.merge(root,a,b);
}
}
}