FHQ-Treap 笔记
前言
众所周知,Treap 速度快但是码量大,Splay 支持的操作多但是懒得背,pb_ds 总是让人不够信任。
此时,我们就需要 FHQ-Treap。
这也是一种 Treap,核心是分裂 (split) 操作和合并 (merge) 操作。优点有码量小、支持区间操作、支持可持久化,缺点也很明显,常数大。
FHQ-Treap
先定义一下数据。
\(ls_p\) 和 \(rs_p\) 分别表示 \(p\) 节点的左右儿子,\(val_p\) 表示 BST 权值,\(ord_p\) 表示 Heap 权值,\(siz_p\) 表示树中以 \(p\) 为根节点的子树大小。
首先说明白,FHQ-Treap 不对 \(val\) 相同的节点进行压缩。
分裂操作
我们现在先只讨论按 \(val\) 分裂。
给定一个权值 \(v\),把树 \(T\) 中所有的节点的 \(val\) 按照 \(v\) 进行分界,\(val\leq v\) 的放到 \(T_x\) 里,其它的放放到 \(T_y\) 里。
Split 完成的就是这个操作。下面我们来考虑如何高效完成它。
假设 \(p\in T,val_p\leq v\),很显然 \(p\) 应该被放进 \(T_x\) 里。因为 BST 的性质,\(ls_p\) 中的节点显然也有 \(val\leq v\),所以 \(p\) 以及 \(ls_p\) 都应该被放进 \(T_x\) 中。此时 \(T_x\) 中的 \(p\) 的左子树是确定的;\(T\) 中除了 \(p\) 的右子树,其它部分应该都被放进了 \(T_x\) 或 \(T_y\) 中。所以下一步,\(T_x\) 中只有 \(p\) 的右子树(此时为空)会加新节点,\(T\) 中只有 \(p\) 的右子树会被考虑分到 \(T_x\) 或 \(T_y\) 中。
\(val_p>v\) 同理。
于是就有了 Split 操作的代码。
void split(int p,int v,int &x,int &y){
if(!p){x=y=0;return;}
if(val[p]<=v)split(rs[p],v,rs[x=p],y);
else split(ls[p],v,x,ls[y=p]);
push(p);
}
此处使用了引用传参,请注意。
合并操作
有两棵树,\(T_x\) 和 \(T_y\),其中 \(\max_{p\in T_x}\{val_p\}<\min_{p\in T_y}\{val_p\}\),我们要把这两棵树合并成一个树 \(T\)。
在此处,需要说明一下我们维护的是大根堆。
我们先从 \(T_x\) 和 \(T_y\) 的根节点 \(x,y\) 考虑。
若 \(ord_x>ord_y\),则在 \(T\) 中 \(x\) 应是 \(y\) 的父亲,再根据 \(val_x<val_y\),则 \(y\) 是 \(x\) 的右儿子。\(T\) 中 \(x\) 的左子树就是 \(T_x\) 中的左子树,因此可以不用管。此时 \(x\) 有右儿子,但 \(y\) 也可能是 \(x\) 的右儿子,所以我们要对 \(rs_x\) 和 \(y\) 递归合并。
\(ord_x\geq ord_y\) 同理。
于是便有了 Merge 操作的代码。
int merge(int x,int y){
if(!x||!y)return x+y;
if(rd[x]>rd[y]){
rs[x]=merge(rs[x],y);
push(x);
return x;
}else{
ls[y]=merge(x,ls[y]);
push(y);
return y;
}
}
其它操作
有了 Split 和 Merge,这些操作就十分好写了。
插入
若插入的节点为 \(p\),直接把 \(T\) 按 \(val_p-1\) 分裂成 \(T_x\) 和 \(T_y\),\(T_x\) 与 \(p\) 合并再与 \(T_y\) 即可。
删除
把 \(T\) 按 \(v-1\) 分裂成 \(T_x\) 和 \(T_z\),把 \(T_z\) 按 \(v\) 分裂成 \(T_y\) 和 \(T_z\),则 \(T_y\) 中所有节点的权值等于 \(v\),我们直接依次合并 \(T_x,ls_y,rs_y,T_z\) 即可。
排名
把 \(T\) 按 \(v-1\) 分裂成 \(T_x\) 和 \(T_y\),则 \(siz_y+1\) 即为排名。
第 k 大、前驱后继
同普通平衡树。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效