各种平衡树比较
| 平衡树 | 附加域 | 平衡性 | 效率 | 编程难度 | 实用性 | 特点 |
| :—: | :–: | :——: | :——: | :–: | :–: | :–: |
| Treap | 修正值 | 较好 | 较快 | 易 | 好 | 随机平衡 |
| Splay | - | 玄学(均摊 中) | 玄学(均摊 中) | 中 | 好 | 灵活易变 |
| SBT | 子树大小 | 好 | 快 | 中 | 好 | 短小精悍 |
| BST | 无 | 差 | 不稳定 | 易 | 一般 | 编写容易 |
##1 Splay Tree
###1.1 模板
关于写法:
我之前写的是仿照维基百科标程用数组模拟链表写的,其优点是方便初学者理解(当时要给小伙伴们讲课嘛),但是其相当冗长,考试时缺少优势。
我这里写的是简写的非递归版,综合各方面优点,既简短又方便调试。
当然网上还有大神写的递归版,个人认为调试不便。
####1.1.1 普通平衡树
维护一些数,其中需要提供以下操作:
1. 插入 x x x 数;
2. 删除 x x x 数(若有多个相同的数,因只删除一个);
3. 查询 x x x 数的排名(若有多个相同的数,因输出最小的排名);
4. 查询排名为 x x x 的数;
5. 求 x x x 的前趋(前趋定义为小于 x x x,且最大的数);
6. 求 x x x 的后继(后继定义为大于 x x x,且最小的数)。
注意:本代码中如果找不到 lowerBound 或 upperBound 会返回 0 ,具体使用时要根据需要更改。
1 |
|
####1.1.2 文艺平衡树
区间翻转
1 |
|
###1.2 题目
####1.2.1 HNOI2004 宠物收养所
最近,阿Q开了一间宠物收养所。收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物。每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得出该领养者希望领养的宠物的特点值 a ( a 是一个正整数, a<$2^{31}$ ),而他也给每个处在收养所的宠物一个特点值。这样他就能够很方便的处理整个领养宠物的过程了,宠物收养所总是会有两种情况发生:被遗弃的宠物过多或者是想要收养宠物的人太多,而宠物太少。
1. 被遗弃的宠物过多时,假若到来一个领养者,这个领养者希望领养的宠物的特点值为 a ,那么它将会领养一只目前未被领养的宠物中特点值最接近a的一只宠物。(任何两只宠物的特点值都不可能是相同的,任何两个领养者的希望领养宠物的特点值也不可能是一样的)如果有两只满足要求的宠物,即存在两只宠物他们的特点值分别为 a-b 和 a+b ,那么领养者将会领养特点值为 a-b 的那只宠物。
2. 收养宠物的人过多,假若到来一只被收养的宠物,那么哪个领养者能够领养它呢?能够领养它的领养者,是那个希望被领养宠物的特点值最接近该宠物特点值的领养者,如果该宠物的特点值为 a ,存在两个领养者他们希望领养宠物的特点值分别为 a-b 和 a+b ,那么特点值为 a-b 的那个领养者将成功领养该宠物。
一个领养者领养了一个特点值为 a 的宠物,而它本身希望领养的宠物的特点值为b,那么这个领养者的不满意程度为 abs(a-b) 。
你得到了一年当中,领养者和被收养宠物到来收养所的情况,希望你计算所有收养了宠物的领养者的不满意程度的总和。这一年初始时,收养所里面既没有宠物,也没有领养者。
求不满意度之和
1 |
|
####1.2.2 [HNOI2002] 营业额统计
Tiger 最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况。Tiger 拿出了公司的账本,账本上记录了公司成立以来每天的营业额。分析营业情况是一项相当复杂的工作。由于节假日,大减价或者是其他情况的时候,营业额会出现一定的波动,当然一定的波动是能够接受的,但是在某些时候营业额突变得很高或是很低,这就证明公司此时的经营状况出现了问题。经济管理学上定义了一种最小波动值来衡量这种情况: 该天的最小波动值。
当最小波动值越大时,就说明营业情况越不稳定。
而分析整个公司的从成立到现在营业情况是否稳定,只需要把每一天的最小波动值加起来就可以了。你的任务就是编写一个程序帮助 Tiger 来计算这一个值。 第一天的最小波动值为第一天的营业额。
天数 $n\leq32767$,每天的营业额 $a_i\leq10^6$
最后结果 $T<=2^{31}$
这道题纯属是练手的,对这道题不必想如何统计前面树中的最小值,因为此题是要每个数都要插入的,插入完之后此节点一定是根节点,那么只要取右子树中的最小值和左子树中的最大值与此节点的差值即可。最难的删除操作在此题也不涉及。
唯一的不同点就是这道题的数据不保证没有重复,所以插入的时候如果发现此数值已经在树中就不必插入了。
1 |
|
##2 Treap
###2.1 模板
注意:本代码中如果找不到 lowerBound 或 upperBound 会返回 0 ,具体使用时要根据需要更改。
####2.1.1 普通平衡树
1 |
|
####2.1.2 二逼平衡树
维护一个有序数列,其中需要提供以下操作:
1. 查询 x x x 在区间内的排名;
2. 查询区间内排名为 k k k 的值;
3. 修改某一位置上的数值;
4. 查询 x x x 在区间内的前趋(前趋定义为小于 x x x,且最大的数);
5. 查询 x x x 在区间内的后继(后继定义为大于 x x x,且最小的数)。
由于要操作区间,考虑在平衡树外套线段树,由于 splay 易被卡然后 TLE,这里用 Treap。
树套树中的一大难点就是分配内存问题。首先,应注意到如果将 Treap 完全封装到 class 里面的话会 MLE ,正确的做法是统一分配 Treap 节点;然后,整个线段树上每个节点都有的 Treap 一共有 $Nlog_2N$ 个节点,而我们删除一个 Treap 中的节点并未在内存中删除,所以我们对于每次更改操作需要 $log_2N$ 个节点,这又是 $Nlog_2N$ 个,一共需要 $2Nlog_2N$ 个 Treap 节点,所以需要开 $1.5*10^6$ 左右我就是因为这个 RE 了。
1 |
|