区间处理和统计是OI中一类重要的题型,而这类题目通常需要用高级数据结构维护,这里就先总结一下本人学习过的可以用于区间处理和统计的高级数据结构。
注意,本人此处不提供各个数据结构的详细介绍或教学,详细教学请自行搜索。
一、线段树
线段树可以说是区间处理和统计问题的基础,它支持静态区间的维护,支持区间修改和区间询问。但维护的信息要求有可区间合并性,就是指一个区间的信息可以由它分割成的两个区间的信息合并得到,比如线段树可以维护区间和,区间极值,最大子段和,最长连续段等等。
由于线段树的特殊性质,也可以用很优美的O(logn)复杂度解决一些奇怪的问题,例如POJ3667中要求找出位于最左端的连续若干个0,这就可以用线段树的性质进行二分查找。
线段树能解决的经典问题有:序列的逆序对数等。
线段树的一个有用的扩展就是二维线段树,就是在母线段树(第一维)的每个节点再存储一棵子线段树(第二维),从而实现矩阵的维护。线段树还可以在树链剖分(也被称为路径剖分,或基于链的树上分治)中起到维护重链的作用,使其可以维护静态树结构的路径信息和子树信息。除此之外,线段树的可持久化也是一个非常有用的扩展,这个后面再谈。
二、平衡树
线段树之所以是基础,是因为还有很多其不能解决的问题,例如:要求支持在区间中插入或删除元素,或者询问整体第K小的元素,这就使线段树束手无策了。这时就要请出一个更为灵活的数据结构:平衡树。平衡树有多种写法:SBT,treap,splay等,但多用于区间处理的还是splay(伸展树)。伸展树和线段树最大的不同就是可以支持插入和删除元素,而且基本上包含了线段树可以解决的所有问题,是一个非常优秀的数据结构。
伸展树能解决的经典问题有:查询整体第K小值,查询元素排名等。
伸展树在LCT(Link-Cut Tree)中起到维护偏爱链的作用,类似于线段树在树链剖分中维护重链。类比于伸展树与线段树,LCT对于树链剖分来说也更灵活了,可以支持动态树结构的维护,这里就不展开了。
三、树状数组
树状数组是一个线段树的精巧替代品,代码复杂度低,可以起到维护动态前缀和的作用,其维护区间和的方式就是用前缀和相减,这也就说明树状数组只能维护有可减性的信息,例如区间和有可减性,而区间极值没有可减性(也就是说,不能通过两个前缀极值的操作来求出一个区间的极值),所以树状数组的作用域很小,一般不是不可或缺。
树状数组能解决的经典问题有:方便地维护动态前缀和。
四、可持久化线段树(主席树)
可持久化线段树就是线段树的可持久化版本。什么叫可持久化?就是支持历史版本的查询,例如:询问第K次修改后的区间和,这时用多棵线段树存储历史版本显然就硬伤了,而可持久化线段树就专门解决了这一问题。因为每次修改都不会修改超过logn个节点,所以我们只需每次修改时新开logn个节点就可以了。而一些看起来像线段树,又不能用线段树解决的题目,就要考虑是不是可以转化为对历史版本的维护和访问。例如查询区间第K小,就可以将插入一个元素看成一个单点修改,那么维护区间内都有哪些数时就可以用后面的版本减去前面的版本得到。是不是很熟悉?是的,这和前缀和的思想是差不多的。
可持久化线段树能解决的经典问题有:在线查询区间第K小,带单点修改在线查询区间第k小,区间中位数,区间众数等。
至于带单点修改的在线查询区间第K小我有写过,是用树状数组套主席树,再次证明树状数组维护动态前缀和的优越性。
五、莫队算法
莫队算法只能解决离线的区间查询问题,但能解决一些以上数据结构都无法解决的一些问题,维护的信息要求是能在较短的时间内(O(1)~O(logn))由一个区间的信息转移到插入或删除一个元素后区间的信息。O(n*sqrt(n))的时间复杂度虽然比以上数据结构都差一筹,但是代码复杂度较低,且思考难度不大,在考场上时间紧迫的情况下可以考虑采用这种方法。
莫队算法能解决的经典问题有:区间众数等。
我们发现一些主席树能解决的问题,莫队算法也能解决,因为它们的思想本质上都是类似的,只是主席树中将这个信息转移的过程看做单点修改,但如果这个转移无法看做线段树上的修改,就是莫队算法大显身手的时候了。
就先总结到这,等以后学到了更多再补充吧~