点分治 & 点分树
点分治
点分治适合处理大规模的树上路径信息问题。
问题引入:
给定一棵有 \(n\) 个点的树,两条边之间有边权,询问树上距离为 \(k\) 的点对是否存在。
流程如下:
- 选择当前树的重心作为根结点 \(root\),所有完全位于其子树的路径可以分为两种,一是经过 \(root\) 的路径,二是不经过 \(root\) 的路径,显然,对于第一种路径,我们可以通过枚举其子节点来得到这些路径的答案;对于第二种路径我们可以通过递归 \(root\) 子树来得到答案,即步骤 2。在步骤 1 最后,我们将 \(root\) 标记为不可到达。
- 对于 \(root\) 的每一个直接相连的子节点构成的子树中,我们分别递归处理其子树。
- 重复 1、2 两个步骤,直到所有结点都成为过 \(root\)。
时间复杂度 \(\mathcal O(n \log n)\)。
正确性证明:
对于任意一条路径 \(u_1 \to u_2 \to \dots \to u_k\),设路径中在点分治中最早遍历到的点为 \(u_w\),显然,这条路径一定会在 \(u_w\) 为 \(root\) 时被计算入答案,又考虑到 \(u_w\) 在步骤 1 后会被标记为不可到达,故这条路径当且仅当在 \(u_w\) 为 \(root\) 时会被计算入答案。
时间复杂度证明:
考虑到每次选择的 \(root\) 均为子树的重心,故只会有 \(\log n\) 层,每一层统计答案 & 找重心的总复杂度为 \(\mathcal O(n)\),故总时间复杂度为 \(\mathcal O(n \log n)\)。
点分树
问题引入:
给定一棵有 \(n\) 个点的树,点有点权,有 \(q\) 次操作,每次操作修改一个点权或询问所有与点 \(x\) 距离小于等于 \(k\) 的点的点权之和。
分析:
对于每次询问跑点分治显然行不通,我们发现每次点分治都会浪费时间在找重心 & 统计无用的答案,我们考虑把点分治的过程记录下来,即构造一棵点分树。
构造点分树流程:
流程很简单,选择当前树的重心作为 \(root\),对于 \(root\) 的每一个直接相连的子节点构成的子树中,我们分别找到其子树的重心作为新的 \(root’\),将 \(root'\) 在点分树上的父亲定为 \(root\),将 \(root\) 标记为不可到达,分别对子树递归处理即可。
性质:
- 每一个结点均会出现在点分树中出现。
- 树高为 \(\log n\) 级别。
- 对于任意两点 \(u, v\),在点分树上的 LCA (设为 \(w\))一定在原树 \(u \to v\) 的路径上,即 \(\operatorname{dist}(u, v) = \operatorname{dist}(u, w) + \operatorname{dist}(w, v)\)。
由以上性质,我们便可以通过 \(x\) 在点分树上跳父亲结点来找到所有经过 \(x\) 的路径。
对于此问题,我们便可以通过维护动态开点值域线段树 + 容斥便可在 \(\mathcal O(n \log n + q \log^2 n)\) 完成此题。