FHQ-Treap
\(1.\)平衡树的定义,性质
平衡树,顾名思义就是所有子树大小较为均衡的二叉搜索树,其特性在于对于每次到叶子节点的操作,其期望复杂度是\(O(\log n)\)的,这样我们就能在可接受复杂度内完成一次操作
\(2.\text{Treap}\)的结构及其特性
\(\text{Treap=Tree+Heap}\) 是一个二叉搜索树和堆的结合体
\((1).\)二叉搜索树
即一颗二叉树,满足对于任意的节点,其左子树的所有权值均小于其自身权值,其右子树权值均大于其权值(左右子树均存在)称为一颗二叉搜索树\(\text{(Binary Search Tree)}\)
性质:对\(\text{BST}\)进行中序遍历,得到的序列一定是严格递增的,反之,若一棵树的中序遍历递增,其为二叉搜索树。
\((2).\)堆\((\text{Heap})\)
为一颗完全二叉树,以大根堆为例其满足对于任意节点,其权值恒大于左右儿子,根节点储存全局最大值,小根堆反之
\(\text{3.FHQ-treap}\)
\((1).\)对于树上的每个节点,我们维护以下的信息
\((\text i).lc,rc\) 该节点的左右儿子编号
\((\text{ii}).val\) 该节点的权值,符合二叉搜索树结构
\((\text{iii}).key\) 一个随机值,符合堆的性质,以此来满足平衡性
\((2).\text{FHQ-treap}\)是一种非旋\(\text{treap}\)其功能基于两个函数实现
\(\text{Function1: Split:}\)
该函数的功能是将整颗\(\text{Treap}\)按某个权值\(val\)分成两个,满足左侧\(\text{Treap}\)的权值\(\le val\),右侧全部\(> val\),该函数的实现过程是这样的
\(\text{Case 1 :}\)若当前节点的值\(\le val\),即该节点应被划分到左侧,其左子树显然也应该被分到左侧,然后递归右子树
\(\text{Case 2 :}\)若当前节点的值\(> val\),即该节点应被划分到右侧,其右子树显然也应该被分到右侧,然后递归左子树
void split(int u,int v,int &x,int &y){
if(!u){x=y=0;return;}
if(t[u].v<=v){
x=u;split(t[u].rc,v,t[u].rc,y);
}else{y=u;split(t[u].lc,v,x,t[u].lc);}
pushup(u);
}
\(\text{Function2:Merge:}\)
该函数即将两个\(\text{Treap}\)按根节点的\(key\)合并,并使其满足堆的性质,其本质是将两个区间首尾相接,所以\(\text{Merge}\)函数实际上是对于任意的两段区间都可以进行,由此可见\(\text{FHQ-treap}\)实际上维护的是一段序列
注意:在使用以下的代码时,务必保证\(x\)树内的最大值小于\(y\)树内的最小值
int merge(int x,int y){
if(!x||!y) return x+y;
if(t[x].key>t[y].key){//维护Heap结构
t[x].rc=merge(t[x].rc,y);//维护BST结构
pushup(x);return x;
}else{
t[y].lc=merge(x,t[y].lc);
pushup(y);return y;
}
}
\((3).\text{FHQ-treap}\)的其他功能
\(\text{(i). insert}\) 插入数值\(val\)
即将原树\(split\)为\(\le val,>val\)两棵树,插入一个值为\(val\)的新节点,\(merge\)即可
\(\text{(ii). erase}\) 删除数值为\(val\)的元素中的一个/全部
一个:分裂为\(<val,=val,>val\)三棵子树,删除\(=val\)中的根节点,依次合并即可
全部:分裂同上,合并时直接合并\(>val,<val\)两子树
\(\text{(iii). rank}\) 返回排名为\(k\)的元素的数值
本质上是平衡树上二分,维护左右子树的\(sz\)信息,并使用\(pushup\)操作去维护,如果左子树\(sz\ge k\),递归左子树,否则\(k-=sz_{lc}\)若此时\(k=1\),返回该节点权值,否则递归右子树
\(\text{(iV). pre}\) 输出数值为\(val\)的前驱
分裂为\(<val,\ge val\) 两子树,在\(<val\)子树中不断递归右节点,直至空,返回最右侧叶子节点,后继与之类似
$\text{Code}$
mt19937 rnd(time(0));
struct node{
int lc,rc,v,key,sz;
void init(int val){v=val;key=rnd();sz=1;}
}t[N];int tot,rt;
int newnode(int val){
tot++;t[tot].init(val);
return tot;
}//建节点
void pushup(int u){t[u].sz=t[t[u].lc].sz+t[t[u].rc].sz+1;}
void split(int u,int v,int &x,int &y){
if(!u){x=y=0;return;}
if(t[u].v<=v){
x=u;split(t[u].rc,v,t[u].rc,y);
}else{y=u;split(t[u].lc,v,x,t[u].lc);}
pushup(u);
}
int merge(int x,int y){
if(!x||!y) return x+y;
if(t[x].key>t[y].key){
t[x].rc=merge(t[x].rc,y);
pushup(x);return x;
}else{
t[y].lc=merge(x,t[y].lc);
pushup(y);return y;
}
}
void insert(int val){
int x=0,y=0;
split(rt,val,x,y);
rt=merge(x,merge(newnode(val),y));
}
void erase(int val){
int x=0,y=0,z=0;
split(rt,val,x,y);
split(x,val-1,x,z);
z=merge(t[z].lc,t[z].rc);
rt=merge(x,merge(z,y));
}
int lessthan(int val){
int x=0,y=0;
split(rt,val-1,x,y);
int k=t[x].sz+1;
rt=merge(x,y);return k;
}//有多少个元素比他小
int getrank(int k){
int u=rt;
while(u){
if(t[t[u].lc].sz>=k) u=t[u].lc;
else{
k-=t[t[u].lc].sz;
if(k==1) return t[u].v;
else k--,u=t[u].rc;
}
}
}
int pre(int val){
int x=0,y=0;
split(rt,val-1,x,y);
int z=x;
while(t[z].rc) z=t[z].rc;
rt=merge(x,y);
return t[z].v;
}
int nxt(int val){
int x=0,y=0;
split(rt,val,x,y);
int z=y;
while(t[z].lc) z=t[z].lc;
rt=merge(x,y);
return t[z].v;
}
\(4.\)序列\(\text{FHQ-treap}\)
即将上述的权值平衡树中的所有\(val\)换为数组下标,可以利用类线段树的\(Lazy tag+pushdown\)操作进行类似区间加,区间反转之类操作
区间反转:
即打上懒标记,下传时交换该节点的左右子树,类似\(\text{01-trie}\)中的按位取反操作
posted on 2026-08-05 14:10 Underthetwilight 阅读(8) 评论(0) 收藏 举报
浙公网安备 33010602011771号