喵~
猫树
你肯定想知道它为什么要叫猫树(Cat Tree),其实就像珂朵莉树(Old Driver Tree)一样,只是创造的人喜欢罢了
问题
先看猫树用来解决什么问题,一句话说,就是不带修改的线段树
线段树的时间为 $O(n) + O(\log n)$ (预处理 + 单次询问),而猫树可以做到 $O(n \log n) + O(1)$ ,但是不带修改(感觉好没用啊),一般用于优化dp之类的
思路
思路非常简单:由于不带修改,可以线段树上每一段信息统计出来,直接调用
预处理
先看预处理,类似线段树的 build
,我们用递归的方式预处理
设处理到区间 $[l, r]$ ,就维护好其信息(这个信息不同于线段树的整体,而是更细致的信息,且一般以 $mid$ 为界,比如线段树维护“区间和”,猫树这里就应统计 $[l, mid]$ 的后缀和与 $[mid + 1, r]$ 的前缀和),然后进入左右段,时空明显 $O(n \log n)$ (假设统计信息是 $O(n)$ 的)
询问
这才是精髓,看看它是如何做到 $O(1)$ 的
如果只是预处理了,它的询问还是要递归,这不还是 $O(\log n)$ ?这可不行
我们逆向思考,如果是从叶节点 $[x, x]$ 和 $[y, y]$ 向上走,那它们第一次相遇于LCA处时,对应的区间就是唯一一个满足 $l \le x \le mid \le y \le r$ 的区间,而我们在这个区间统计的信息就足以回答询问了(比如“区间和”,我们统计的前后缀足以回答 $[x, y]$ 的和)
那么问题变成树上LCA,而且是完全二叉树,倍增是 $O(\log \log n)$ ,ST表可以做到 $O(1)$ ,但如果只是这样,似乎略显麻烦,体现不出猫树的优势啊
注意到,猫树它和线段树一样,采用“父子二倍”标号,而 $(110101)_2$ 和 $(110011)_2$ 的LCA就是最长公共前缀 $(110)_2$ ,大家可以手玩验证一下
那么怎么快速求出两个数的二进制的最长公共前缀?
求法是有的,但都不是很简单(太麻烦了还不如用ST表),我们退而求其次
设两个数的二进制的最长公共前缀长度为 $len$ (二进制下),发现两个数异或之后,最高位右移了 $len$ 位,而一个数的二进制长度就是 $\log n + 1$ ,可以预处理出 $\log$ $O(1)$ 得LCA的二进制长度
那长度有啥用呢?别忘了,在“父子二倍”标号下,长度就是层数啊!我们求得了LCA在树上是第几层
那有什么用呢?别忘了我们还知道查询的端点 $x, y$ ,考虑在预处理时把同一层的信息直接存在同一个数组里,如让 sum[x][i]
代表第 $x$ 层的前缀( $i > mid$ )/后缀( $i \le mid$ )和,由于一层的节点没有重复覆盖,这是可以做到的
例题
还是给道题:GSS5
1 |
|