17

线段树可以用来维护序列问题。如果要维护的对象从序列变成了树,但是我还想用线段树维护,应该怎么办呢?

一种方法是强行化归——把树划分成若干条链,再用线段树去维护这些链——也就是树剖。

考虑更加聪明一点的方法。线段树可以换一种表述方式,它也是「合并区间」这一操作的重构树。树上的每个节点,都对应了一个在合并过程中产生的区间。特别地,叶子节点对应的是单点。

把这个从序列对应到树上,我们希望构建出一棵新树,它是「合并连通块」这一操作的重构树。并且它的深度最好不要太大,否则就不能保证复杂度了。

这个想法看起来很完美啊,我们来试一试用它解决问题。比如【模板】动态 DP。我们来考虑一下要在新树的节点上维护哪些信息。

……完蛋了,如果连通块有 \(k\) 个叶子,那就得记录 \(2^k\) 个 dp 的状态,分别表示这些节点选或不选的最大权独立集大小。

考虑线段树为什么能做链上的最大权独立集问题,这是因为「区间」的端点数量是 \(O(1)\) 的,所以也就只用记录 \(O(1)\) 个点的选择状态。但是「连通块」的端点数量不是 \(O(1)\) 的,就炸缸了。

但是其实我们不用记录连通块的所有叶子的选择状态。我们只用记录那些「与外界有连边」的叶子的选择状态。注意到我们是可以自由选择怎么去合并连通块的,如果能选择一种合并方式,使得这个过程中产生的任意一个连通块都只包含 \(O(1)\) 个「与外界有连边」的节点——也就是只有 \(O(1)\) 个端点,那就全对了。

其实这个 \(O(1)\) 可以恰好是 \(2\)。假设有两个连通块 \(A,B\)

  1. 如果 \(A\) 的端点是 \(x,y\)\(B\) 的端点是 \(x,z\),且 \(z\) 不是任意其他连通块的端点,那可以将 \(A,B\) 合并为 \(C\),端点是 \(x,y\),这一操作称为 Rake;
  2. 如果 \(A\) 的端点是 \(x,y\)\(B\) 的端点是 \(y,z\),且 \(y\) 不是任意其他连通块的端点,那可以将 \(A,B\) 合并为 \(C\),端点是 \(x,z\),这一操作称为 Compress。

其实就是删一度点、缩二度点。

容易验证按照以上规则合并出来的连通块都只有不超过 \(2\) 个端点。

看起来好像只需要 Rake 操作就能完成整个合并过程啊,那为什么还要 Compress 操作?这是因为只用 Rake 操作无法保证重构树的深度。考虑怎么建出一棵深度有保证的重构树:

初始有 \(n-1\) 个连通块,每个连通块都包含 \(2\) 个点和 \(1\) 条边。先对树重链剖分,然后对于每条重链:

  1. 对于链上的每个点 \(x\),都先递归其所有轻子树,再把所有轻子树使用 Rake 操作合并到 \((x,son_x)\) 这一连通块上;
  2. 使用 Compress 操作将这条重链合并成一个连通块。

这两步都需要类似分治的结构去合并。如果分治中点取 \(mid\),那重构树的树高就是 \(O(\log^2n)\) 的;如果取的是连通块大小的带权中点,那树高就是 \(O(\log n)\) 的。

然后就没有问题了,这个东西就真的可以用来维护动态树上最大权独立集了。

这个东西也叫静态 Top Tree。

动态 Top Tree 我还在学习中。


我没学动态 Top Tree,我去学广义串并联图了。

考虑怎么把「合并连通块」的过程从树拓展到一般图上。在 Rake 和 Compress 之后,我们再加入第三种操作:

  • 如果 \(A\) 的端点是 \(x,y\)\(B\) 的端点也是 \(x,y\),那可以将 \(A,B\) 合并为 \(C\),端点还是 \(x,y\),这一操作称为 Twist。

但是不是所有图都能通过这三种操作合并为一个连通块的。能合并的图,就叫做广义串并联图。

容易发现,一个图是广义串并联图,等价于其不存在同胚于 \(K_4\) 的子图。

能不能对广义串并联图建出树高为 \(O(\log)\) 的重构树?其实是不能的。他的重构树树高理论上界是 \(O(n)\) 的。

这件事情听起来太让人难过了,因为这意味着我们没法用类似 Top Tree 的手段去快速维护图上的信息了。

但是他还是很能做单次询问的。

posted @ 2025-12-31 15:18  Egg_eating_master  阅读(8)  评论(0)    收藏  举报