fhqTreap 学习笔记
前言
首先 sto fhq orz。
以前有一篇【笔记】平衡树,但是只讲了 BST 和 Splay(还没有讲全)。今天补一补 神·fhq 发明的 fhqTreap。
关于 fhqTreap
fhqTreap 又称为 无旋Treap。顾名思义,就是 “不用旋转的Treap”。
fhqTreap 仅仅使用 分裂(split) 和 合并(merge)来支持其他平衡树拥有的操作。
fhqTreap 详解
操作数据 \(\verb! (data)!\)
...
const int N=1e5+10;
...
private:
#define lc(rt) son[rt][0]
#define rc(rt) son[rt][1]
int cnt=0;
int a[N], sz[N], rk[N], son[N][2];
...
public:
int root=0;
...
...
\(\verb!def: lc(rt) :=son[rt][0]!\):左儿子。
\(\verb!def: rc(rt) :=son[rt][1]!\):右儿子。
\(\verb!var: cnt !\):结点数。
\(\verb!arr: a[N] !\):结点的值。
\(\verb!arr: sz[N] !\):以结点为根的子树大小。
\(\verb!arr: rk[N] !\):结点的优先级。
\(\verb!var: root !\):……
新建结点 操作 \(\verb! (node)!\)
根据定义自行理解。
int node(int val){
sz[++cnt]=1;
a[cnt]=val;
rnk[cnt]=rand();
return cnt;
}
合并 操作 \(\verb! (merge)!\)
\(\verb!merge!\) 操作就是合并一下根节点和新建节点 然后保留根/替换根(\(rk\) 决定)
反正是随机合并 期望树高 \(\log n\)。
int merge(int x, int y){
if(!x||!y) return x|y;//空树
//根据 rk 决定
if(rnk[x]<rnk[y]){
rc(x)=merge(rc(x),y);
pushup(x);
return x;
}
else{
lc(y)=merge(x,lc(y));
pushup(y);
return y;
}
}
分裂 操作 \(\verb! (split)!\)
\(\verb!split!\) 实现的操作大概是 把一棵树分成俩…然后左边的值小于等于 \(val\) 右边的值大于 \(val\)。
如果你 split(rt,val,x,y)
那么你就把 \(rt\) 分成两个部分 \(x,y\) 了 其中 \(x\) 的子树的 \(v_x\leq val\) , \(y\) 的子树的 \(v_y>k\)。
void split(int rt, int val, int& x, int& y){
if(!rt){x=y=0;return;}//空
//根据 val 决定
if(a[rt]<=val){
x=rt;
split(rc(x),val,rc(x),y);
}
else{
y=rt;
split(lc(y),val,x,lc(y));
}
pushup(rt);//记得更新
}
查找第 \(k\) 小 \(\verb! (kth)!\)
可以按照 \(sz\) 来找 \(\operatorname{kth}\)。 递归/非递归都行。
int kth(int rt, int k){
if(k<=sz[lc(rt)]) return kth(lc(rt),k);
if(sz[lc(rt)]+1==k) return a[rt];
return kth(rc(rt),k-sz[lc(rt)]-1);
}
查找排名为 \(x\) 的数 \(\verb! (rnk)!\)
\(\verb!rank!\) 的话就分离一个 \(val=k-1\) 的子树。然后求这个子树的 \(sz\) 就知道 \(\verb!rank!\) 了。
int rnk(int val){
int x, y, res;
split(root,val-1,x,y);
res=sz[x]+1;
root=merge(x,y);
return res;
}
其他操作
和 \(\verb!rnk!\) 差不多,不讲了。
代码实现
class fhq{
private:
#define lc(rt) son[rt][0]
#define rc(rt) son[rt][1]
int cnt=0;
int a[N], sz[N], rnk[N], son[N][2];
//新建结点
int node(int val){
sz[++cnt]=1;
a[cnt]=val;
rnk[cnt]=rand();
return cnt;
}
void pushup(int rt){
sz[rt]=sz[lc(rt)]+sz[rc(rt)]+1;}
//按照BST的性质合并
int merge(int x, int y){
if(!x||!y) return x|y;
if(rnk[x]<rnk[y]){
rc(x)=merge(rc(x),y);
pushup(x);
return x;
}
else{
lc(y)=merge(x,lc(y));
pushup(y);
return y;
}
}
//将rt分裂成x和y (其中vx<=val,vy>val)
void split(int rt, int val,
int& x, int& y){
if(!rt){x=y=0;return;}
if(a[rt]<=val){
x=rt;
split(rc(x),val,rc(x),y);
}
else{
y=rt;
split(lc(y),val,x,lc(y));
}
pushup(rt);
}
public:
int root=0;
//插入一个点
void insert(int val){
int x, y;
split(root,val,x,y);
root=merge(merge(x,node(val)),y);
}
void erase(int val){
int x, y, z;
split(root,val,x,z);
split(x,val-1,x,y);
y=merge(lc(y),rc(y));
root=merge(merge(x,y),z);
}
int kth(int rt, int k){
if(k<=sz[lc(rt)])
return kth(lc(rt),k);
if(sz[lc(rt)]+1==k) return a[rt];
return kth(rc(rt),k-sz[lc(rt)]-1);
}
int ranks(int val){
int x, y, res;
split(root,val-1,x,y);
res=sz[x]+1;
root=merge(x,y);
return res;
}
int pre(int val){
int x, y, res;
split(root,val-1,x,y);
res=kth(x,sz[x]);
root=merge(x,y);
return res;
}
int suf(int val){
int x, y, res;
split(root,val,x,y);
res=kth(y,1);
root=merge(x,y);
return res;
}
}t;
练习题:【模板】普通平衡树