前言
找了半天网上的资料,说实话真的不好找哎,然后对着硕果仅存的几篇博客理解了半天。。。。依旧没有弄出什么结论来。
于是嘛。。。就只有对着以前抄的代码理解咯。。发现还是有效果的
基本原理
如果你用平衡树维护集合都不会的话,那你可以出门左拐,看看我的其它平衡树学习笔记
这里造成的一个很大的理解障碍就是它的确和用平衡树维护集合有区别的,其中最大的区别就是维护序列的时候给每个结点挂了一个权值,而这个权值对于这个树的形态是无关紧要的(干脆说丝毫没有干系),而这个权值就是序列上每一个元素的值。
那么原来维护集合的平衡树的权值对应的是什么呢?对应的其实就是下标,把平衡树中序遍历一遍之后就得到了序列,而维护集合的时候中序遍历可以得到有序的元素值,想一想序列中什么是有序的?就是下标。
但是又由于这个下标比较有规律,我们也就不单独对它进行记录了,因此看起来和维护集合的平衡树变量差不多,实际上呢含义是不一样的。
那么先不管Heap,要保证平衡树的多种可能的组合才能使得Heap的出现不会错误,比如有序列a[1],a[2],那么就有两种情况a[1]为a[2]的左儿子或者a[2]为a[1]的右儿子,那么情况就很多了哈。
所以heap的出现应该可以使得平衡树形态唯一,不过也不是很确定哎。
总之就是不要受了维护集合的那种平衡树的影响,也就是在理解不通的时候不要类比学习,而是重新理解,当理解得足够清楚的时候回过头来才会发现它们的正确关系。
基本操作
- 分裂:按照元素个数进行分裂。(这样做有好处)
- 合并:根据hreap的关系进行合并,有点像可并堆的那种操作,有三种情况哦。
这两个都是递归结构,所有操作都是基于这两个操作的,要注意分裂和合并的两个序列是有位置关系的,左边那个序列的下标也在左边,千万不能搞混。
难道还有可以搞混的东西吗??
其实。。。好像是有的,heap的符号判断似乎可以。。。随便搞。。。反正就是为了随便搞嘛。。。
还有一些奇怪的操作
- 序列反转:把该子树所有儿子全部左右交换
注意几个细节
pushup的位置:Merge的时候根结点,Split的时候根结点
pushdown的位置:Merge访问的结点,Split访问的now结点
感觉操作都在merge和split两个基本操作中弄完了是不是好开心!!
此外,旋转操作标记的时候不需要对当前结点进行操作,因为该标记本身就是作用于子结点的嘛。
还有就是实现的时候一般来说习惯写小根堆
代码
//这里是实现求最值,区间加法,以及反转操作的。
//附建立完全平衡的二叉树,快一点,但不是treap的本意(反而像替罪羊树),直接往上面挂是完全没有问题的。
struct Treap
{
int rt,np,fix[maxn],w[maxn],ch[maxn][2],sz[maxn],flip[ma