在OI中,有些时候我们会遇到一些维护多维信息的题目,比如经典的三维偏序,或者带修改区间k小值
这个时候有的dalao就会跳出来大喊“整体二分!”“CDQ!”
然而这并不是我们今天讨论的重点……并且在强制在线的情况下,上面这两种算法就无能为力了。
那么我们就需要用数据结构乱堆树套树的方法来解决这类问题。这类树套树解法以码量大和难调试著称。
通过用一种(棵?)数据结构维护一维信息,我们可以实现在线地维护多维信息。
那么让我们开始总结一下树套树吧!
一.线段树/树状数组套平衡树
这大概是没接触过树套树的同学接触的第一种树套树类型吧。。。
这种套法一般是用外层的树维护区间信息,在外层树的每一个节点放一棵内层树,内层的树维护权值信息。
比如说查区间第k大,用套平衡树的做法就是二分权值val->到这个区间对应的log个线段树节点上查val的rank值,加起来与目标值进行比较
这种做法的空间需求是O(nlogn)O(nlogn)的,由于每个元素都会在logn个外层树节点中插入自己
而时间复杂度上,除了查询区间第k大,每次操作是O(nlog2n)的,
由于在logn个外层树上都要用O(logn)O(logn)的时间查询
查询第k大是O(nlog3n)O(nlog3n)的,由于在logn个外层树上都要用O(logn)O(logn)的时间查询。
这种类型的树套树,经典的有:
bzoj3196 二逼平衡树
bzoj3262 陌上花开
bzoj3295 动态逆序对
bzoj2141 排队(和上面那题差不多,就不附代码了)
这些题没有太大的难度,直接按照题意操作即可~
二.线段树/树状数组套权值线段树
如果是套线段树,就是我们常说的“树状数组套主席树”了。
其实这里的内层线段树并不是主席树,而是很多颗权值线段树.
这样套的好处就是让原本静态的主席树变得可以支持修改,因为每次修改只会更改logn级别棵内层树的信息
线段树依然有区间加减性,因此对于某一个给定区间我们可以用log棵权值线段树通过加减来“拼”出对应区间的线段树
所以每一种操作都是$O(nlog2n)$的.
这类树套树的习题有:
bzoj4009 接水果(模型转换之后整体二分or线段树套线段树)
(没打树套树……懒了233)
bzoj1146 网络管理Network(树状数组套主席树上树)
三.平衡树套权值线段树/平衡树
这大概是比较高端的一种套法?
有的时候我们会发现线段树无法胜任套在外面这个要求,比如一个常见的原因是区间长度固定,无法支持区间内的插入。
那么我们就需要外层的数据结构是平衡树了:平衡树可以在中间插入。
并且这树还不能旋转,由于旋转带来的父子关系改变带来的维护会耗费大量的时间。
那么我们的选择只剩下替罪羊树和无旋Treap了。
我们一般使用替罪羊树而不是无旋Treap,因为无旋Treap常数大,并且在split和merge时的信息维护也很慢……
在实际编程时,我们就正常的插入,在替罪羊的节点对应的内层树中插入新点的权值,后面的查询操作根据题意而定。
只要没有玩脱这些操作的复杂度也都是$O(nlog2n)$的。
这种类型的题目有:
bzoj3065 带插入区间k小值 (替罪羊套权值线段树,操作的时候和树状数组套线段树差不多,提取出通过加减可以表达对应区间的根节点组,然后找到答案。)
bzoj3217 ALOEXT(替罪羊套Trie树,和上面类似)
(还没打……明天补坑)
*PS:替罪羊树的思想是很好的,这种重建的思想可以优化很多题目的复杂度。
四.树套树在DP题目中的运用
有一些DP题目的转移会涉及到多维的取值关系,因此我们可以用树套树处理这类题目
我们依然用一种数据结构维护一维,在最内层的数据结构中维护对应的DP有关变量。
当然CDQ也可以干这事啦!
这种类型的题目有:
bzoj4553 序列
bzoj2244 拦截导弹
(我这两道都是拿CDQ打的2333)
五.总结
树套树是我们用来维护多维数据信息的有效工具,可以处理多维信息的复杂问题
当然,在可以离线的情况下,整体二分/CDQ或许会更加优秀,我们要根据具体情况选择方法解决。