维护连通块
现有一棵 n 个节点的树。我们构建一最小的连通块 S(l,r),包含编号在 [l,r] 中的所有节点。多次询问,每次对于任意一对 [l,r],询问关于这连通块的信息。
如果可以离线/可持久化,此类问题有一通用思路:用扫描线维护“r 固定时不同 l 构成连通块的信息”。若 [p,q]⊆[l,r],则 S(l,r)⊇S(p,q);对于一固定的 r,我们维护 l 分别取 r,r−1,⋯,1(向左拓展)时,连通块的增量。对于点 x,我们记录 tr(x) 表示 r 固定时使得 x∈S(l,r) 的最大的 l。假设已经对于 r 维护好了;现在拓展到 r’=r+1。
若 x∈/{r,r+1},则 tr+1(x)=tr(x) 是
- x 在 r 到 r+1 的简单路径上,且
- tr+1(x)=r
的充分必要条件。
考虑任意 l。由于 S 是极小的,故将 r+1“加入”S(l,r) 得到 S(l,r+1),只需找到 r+1 和连通块在树上最接近节点之间的路径,将路径上所有点加入连通块;由树的性质,此路径是通向 [l,r] 中任意节点的必经之路,也即 r,r+1 的最短路径的某一前缀。又当 l=r 时,S(l,r+1) 就是该路径本身;再有 [p,q]⊆[l,r]⟹S(l,r)⊇S(p,q),故本命题得证。
因此,我们需将整条路径上的点的最大出现时间整体覆盖成 tr+1(x)=r(r+1 本身除外,它是 tr+1(r+1)=r+1)。可以使用珂朵莉树和树剖维护连续的 t(x) 相同的一段。设 Φ 表示连续段个数。每次覆盖操作会对 O(logn) 条重链执行“删除若干连续段,加入 O(1) 个连续段”的操作。又 Φ≤n,则 Φ 的总变化量是 O(nlogn) 的。因此若用对数级别有序关联式容器(如 std::map
),总时间复杂度为 O(nlog2n)。
(更多…)