速学 FHQ treap
简介
听说是平衡树的扛把子,好写好调好理解,扩展性强、功能多,常数也不大。
基本
用结构体存点,以分裂、合并为基本操作,不需旋转即可维护 treap 性质。
-
分裂 split
先介绍以值划分:考虑以
用引用实现,那么有如下定义:
void split(u, &x, &y, val)
关于 &x,&y,可以理解为两棵子树分别需要填的位置。
为何这样写?暴力是
给出代码:
inline void split(int u, int &x, int &y, int val){
if(!u) {x = y = 0; return ;}
if(t[u].s <= val) x = u, split(rt, rt, y, val);
else y = u, split(lt, x, lt, val);
psup(u);
}
理解:当前值不大于
-
合并 merge
类似线段树合并,用两个指针遍历两棵树:
int merge(x, y)
不需引用,函数返回为合并后的顶点,主要是为了后面操作方便。
还有一点:由于分裂和合并操作总是成对出现,所以默认
inline int merge(int x, int y){
if(!x || !y) return x + y;
if(t[x].rnd <= t[y].rnd) {t[x].rs = merge(t[x].rs, y), psup(x); return x;}
else {t[y].ls = merge(x, t[y].ls), psup(y); return y;}
}
理解:开头是个小技巧,意思是返回两者中非零的那个;当
(此处维护小根堆)
常见操作
有了 split 和 merge 后便能完成大量操作。
-
插入 x:原树分裂为两部分,将
\le x 部分与x 合并,再把它们与>x 部分合并即可。 -
删除 x:分裂为
\le x 和>x ,\le x 再分为\le x-1 和=x 。剔除=x 部分的根合并其左右儿子,将它回溯地合并为原树即可。 -
求 x 排名:即为
<x 的数量+1 。分裂出来然后siz+1 。 -
求排名为 x:利用平衡树性质二分求解即可,和是什么平衡树没啥关系。
-
求 x 前驱 / 后继:
<x 部分中排名最大的 />x 部分中排名最小的。
进阶
我不到啊。