Treap

因为说太多遍了,所以打算直接写下来。

OI 中常用的平衡树有很多,但是论码量,一般大家也只会选 splay 或者 treap,但是这两个平衡树经常被说常数很大。

splay 的常数主要在维护它的结构上,信息合并上的常数还不错,也没啥好说的,这里就不提了。

这里说说 treap。

很多人说非旋 treap 常数很大,这是因为大家经常用一次 split 两次 merge 来搞一个插入,两次 split 一次 merge 搞一个删除,这就连树的路径都遍历了三遍,而且把平衡树的边各种修改,常数自然就上去了。

这里讲一个方法:
假设随机值是小根堆,对于插入,我们比较随机值,如果随机值比树根小,就把这个树按照插入权值 split 开,然后当成插入节点的两个儿子。
不然我们就根据权值比较,进入对应子树继续插入。

删除我们就找到这个节点,把两个儿子 merge 起来,连上去。

如果你学过旋 treap,这个是基本一样的。这样操作还提供了一些额外的性质:

距离叶子期望距离 \(O(1)\),我们这个节点的期望子树大小 \(O(\log_2 n)\)

前者保证了我们 split, merge 时候的深度很小,是常数时间,瓶颈在于在上方路径的探查,而这一部分效率非常高。

后者则让我们可以用 treap 实现 平衡树套树,动态标号等功能。

然后没啥好说的了。

哦补个证明,只补插入的了。

对于你要插入的数,你先找到它在 treap 上二分出来的链。我们算每个位置停下的概率乘子树大小的和,就是期望子树大小。

在一个节点停下的概率不好算,但是它不大于在这个节点及上面停下的概率。
这个概率就是 \(\frac{1}{size+1}\),size 是子树大小。

然后就是 \(\sum size \times \frac{1}{size+1}\),就是深度,就是 \(O(\log_2 n)\)

对于离叶子距离,大家感受一下就好,反正是对的。

posted @ 2022-08-10 22:51  skip2004  阅读(1544)  评论(0编辑  收藏  举报