FHQ-Treap学习笔记
Wogua_boy
感谢洛谷大佬远航之曲提供的学习资料~
FHQ-Treap是由清华大学的超级大佬范浩强发明的,一种实现简单的,可以支持所有Splay操作的的Treap树。
它只需要两个基础操作,就可以达到splay的所有功能。
split
它的主要思想就是把一个Treap分成两个。
split操作有两种类型,一种是按照权值分配,一种是按照前k个分配。
第一种就是把所有小于k的权值的节点分到一棵树中,第二种是把前k个分到一个树里。
对于我们遍历每一个点:
假如它的权值小于k,那么它的所有左子树,都要分到左边的树里,然后遍历它的右儿子。
假如它的权值大于k,那么它的所有右子树,都要分到右边的树里,然后遍历它的左儿子。
因为它的最多操作次数就是一直分到底,效率就是(O(logn))。
merge
这个就是把两个Treap合成一个,保证第一个的权值小于第二个。
几个基本的平衡树操作
-
insert
插入一个权值为v的点,把树按照v的权值split成两个,再按照顺序merge回去。
-
del
删除权值为v的点,把树按照v分成两个a,b,再把a按照v-1分成c,d。
把c的两个子儿子merge起来,再merge(merge(c,d),b)
-
precursor
找前驱,把root按v-1 split成x和y,在x里面找最大值。
-
successor
找后继,把root按v split成x和y,在y里面找最小值。
-
rank
把root按v-1 split成x和y,排名是x的size。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
int ch[maxn][2];
int val[maxn];//本身权值
int pri[maxn];//随机权值
int sz[maxn];//子树大小
int tot;
void up (int x) {
sz[x]=1+sz[ch[x][0]]+sz[ch[x][1]];
}
int newNode (int v) {
sz[++tot]=1;
val[tot]=v;
pri[tot]=rand();//可以改成自定义随机函数
return tot;
}
int merge (int x,int y) {
if (!x||!y) return x+y;
if (pri[x]<pri[y]) {
ch[x][1]=merge(ch[x][1],y);
up(x);
return x;
}
else {
ch[y][0]=merge(x,ch[y][0]);
up(y);
return y;
}
}
void split (int u,int k,int &x,int &y) {
if (!u) x=y=0;
else {
if (val[u]<=k) {
x=u;
split(ch[u][1],k,ch[u][1],y);
}
else {
y=u;
split(ch[u][0],k,x,ch[u][0]);
}
up(u);
}
}
int kth (int u,int k) {
while (1) {
if (k<=sz[ch[u][0]]) {
u=ch[u][0];
}
else if (k==sz[ch[u][0]]+1) {
return u;
}
else {
k-=sz[ch[u][0]]+1;
u=ch[u][1];
}
}
}
int x,y,z,rt;
int main () {
srand((unsigned)time(NULL));
int _;
scanf("%d",&_);
while (_--) {
int op,tt;
scanf("%d%d",&op,&tt);
if (op==1) {
//插入x
split(rt,tt,x,y);
rt=merge(merge(x,newNode(tt)),y);
}
else if (op==2) {
//删除x
split(rt,tt,x,z);
split(x,tt-1,x,y);
y=merge(ch[y][0],ch[y][1]);
rt=merge(merge(x,y),z);
}
else if (op==3) {
//查询x的排名
split(rt,tt-1,x,y);
printf("%d
",sz[x]+1);
rt=merge(x,y);
}
else if (op==4) {
//查询排名x的数
printf("%d
",val[kth(rt,tt)]);
}
else if (op==5) {
//求x的前驱
split(rt,tt-1,x,y);
printf("%d
",val[kth(x,sz[x])]);
rt=merge(x,y);
}
else {
//求x的后继
split(rt,tt,x,y);
printf("%d
",val[kth(y,1)]);
rt=merge(x,y);
}
}
}