Splay
基本
核心操作:splay(x),把 x 换到根的位置上。
记录信息:
- son[x][0/1]:左右儿子
- f[x]:x 的父亲
- key[x]:x 的数值
- size[x]:x 的子树大小
- recy[x]:x 的值重复的次数
旋转操作


Splay操作
splay 操作通过双旋保证均摊复杂度。
具体证明较为复杂,有兴趣可以参见 tarjan 的论文,这里不再赘述。
过程如下:
设执行 splay(x),y=f[x],z=f[y],那么若 x,y,z 在一条直线上,则分别执行 rotate(y),rotate(x);否则执行 rotate(x),rotate(x)。
若 y 是根,显然执行一遍 rotate(x) 即可。
代码
1 |
|
Treap
简介
不仅要记录之前的信息,还要记录一个随机值 rd[i],表示该点的随机值,要求随机值排成一个大根堆,来保证树的深度。
不过可以选择不记录 father。
代码
1 |
|
FHQ-Treap
这个写过了,这里就不再写了。
文艺平衡树
很经典的一道平衡树模板题。
题目
维护一个序列,支持区间翻转,动态查询每个位置上的数字。
思路
打一个 reverse 区间标记即可。
在需要时下传,最后递归输出左子树->自己->右子树。
代码
1 | /************************************************************** |
[NOI2004] 郁闷的出纳员
题目


思路
由于只有全局加减操作,可以考虑改为更改工资下限(当然新加入员工的时候要算一下他当前对应的工资)。
然后每次操作后找出所有 < 下限的直接扔掉即可。
对于FHQ来说很简单,只要拆开后取其中一遍就可以了。
代码
1 |
|
[SHOI2009]会场预约
题目


思路
题意就是叫你维护一条时间轴,支持2种操作:
删除当前时间轴上所有与新区间有交的区间输出删除个数,然后插入新区间。
查询当前时间轴上的区间个数。
很容易考虑到将所有的区间按照两个端点双关键字排序(先左还是右没有什么影响)。
当插入一个区间时,我们找到最后一个在新区间之前与新区间无交的区间与第一个在新区间后与新区间无交的区间,然后计算它们之间的区间个数,返回后删除,再插入新区间。
查询就直接查询时间轴上的区间个数。
无旋treap(fhq treap)实现方法
首先,我们需要支持查找前驱和查找后继的操作,这是平衡树基础操作,不多赘述。接下来对于找到的前驱与后继的位置,我们在此执行split操作,将整棵treap分为3部分,即为删去的各区间之前,删去的区间,删去的区间之后,然后返回的个数直接就是删去区间这一部分的$root$的$size$,然后再插入新区间后直接与删去区间之前与删去区间之后合并即可。
操作2就直接返回整棵treap的$root$的$size$即可。
代码
1 |
|
[HNOI2012]永无乡
题目


思路
使用启发式合并即可。
启发式合并怎么写呢?
1 | void dfs(int x,int &y) |
这样就好了。
其他的和普通平衡树没什么区别了。
代码
1 |
|
[NOI2005] 维护数列
题目

……









