20240806:点分治,虚树选做
P4178 Tree
题意:给定一棵树,求多少无序对 \((u, v)\) 满足 \(\text{dist}(u, v) \le k\)。
对于树上的路径进行分类:经过根;不经过根。
第二类路径点可以通过递归子树求出。
对于经过根的路径,可以一遍 dfs 求出每个点到根的距离 \(\text{dis}(u)\)。
问题转化为求 \(\text{dis}(u) + \text{dis}(v) \le k\) 且 \(u, v\) 不在同一棵子树的点对数。
如果没有后面子树的限制,可以简单的排序 + 双指针求出,那么只要把每棵子树多算的减掉即可。
单次复杂度 \(O(n\log n)\)。如果直接递归,复杂度高达 \(O(n^2\log n)\)。
考虑 \(rt\) 选取当前树的重心(最大子树最小)。
\(rt\) 的每棵子树大小不大于 \(n/2\),如果存在子树 \(u\) 大于 \(n/2\),以 \(u\) 为重心不会比 \(rt\) 更劣。
因此每次都选重心,树的大小至少减少一半,最多递归 \(O(\log n)\) 层,每层总结点数 \(O(n)\)。
时间复杂度 \(O(n\log^2 n)\)。submission
P9058 [Ynoi2004] rpmtdq
题意:对于一棵树有 \(q\) 次询问:给定 \(l, r\),求 \(\min\limits_{l\le i < j \le r} \text{dist}(i, j)\)。
总共 \(O(n^2)\) 个点对,考虑哪些一定不可能成为答案。
如果 \((x_1, y_1),\ (x_2, y_2)\) 满足 \(x_1 \le x_2 < y_2 \le y_1\) 且 \(\text{dist}(x_2, y_2) \le \text{dist}(x_1, y_1)\)。
那么对于任何询问,选 \((x_2, y_2)\) 一定不劣于选 \((x_1, y_1)\),我们称 \((x_2, y_2)\) 是有效的。
点分治处理路径问题,只统计经过 \(rt\) 的有效点对 \((i, j)\)(不妨钦定 \(\text{dist}(i, rt) \le \text{dist}(j, rt)\))。
设当前在处理点 \(u\),哪些不在同一子树中的点 \(v\) 会与 \(u\) 组成有效点对。
定义 \(pre_u\) 表示满足 \(\text{dist}(v, rt) \le \text{dist}(u, rt)\) 的小于 \(u\) 的最大 \(v\)。
现在证明为什么 \((v, u),\ v < pre_u\) 是没用的。
对于 \(v < pre_u\),有
显然 \((v, u)\) 对于 \((v, pre_u)\) 而言是无效的,与假设矛盾。
定义 \(nxt_u\) 表示满足 \(\text{dist}(v, rt) \le \text{dist}(u, rt)\) 的大于 \(u\) 的最小 \(v\)。同理证明 \((u, v),\ v > nxt_u\) 是没用的。
总结:任意点 \(u\) 只有 \((pre_u, u)\) 和 \((u, nxt_u)\) 是有效的。
但是 \((v, u)\) 还要求在不同子树,假设 \(pre_u\) 恰与 \(u\) 在同一子树,只会使得 \(\text{dist}(pre_u, u)\) 变得更小,因此不需要考虑子树限制。
点 \(u\) 一次只产生 \(O(1)\) 个点对,因此整个分治过程产生 \(O(n\log n)\) 个点对。
将询问离线,转化为二维数点,时间复杂度 \(O(n\log^2 n)\)。
点对 \((i, j)\) 的权值 \(\text{dist}(i, j)\) 可以直接用 \(\text{dist}(i, rt) + \text{dist}(j, rt)\),这样带来的问题是 \(i, j\) 在同一子树的距离是错误的。
我们总能递归到 \((i, j)\) 经过的一个重心,在这个中心上一定能正确的求出 \((i, j)\) 距离或者产生更优点对。
还有一个问题是如何求前驱后继,对于 P9678 来说,用 set 维护足矣,但本题还需进一步卡常。
令 \(d(u) = \text{dist}(u, rt)\),将二元组 \((u, d(u))\) 根据 \(u\) 排序。维护一个 \(d\) 值单调不降的单调栈,可以求出所有前驱。后继同理。
P4292 [WC2010] 重建计划
题意:给定一棵树,求长度属于 \([L, U]\) 的路径的最大价值,路径 \(S\) 的价值定义为 \(\dfrac{\sum_{e \in S} w(e)}{\vert S\vert}\)。
对于一条路径,如果 \(\sum_{e \in S} \bigg(w(e) - \text{avg}\bigg) \ge 0\),说明这条路径的价值一定大于等于 avg。
不难想到二分答案,把每条边都减去 mid 后检查长度属于 \([L, U]\) 的最大路径和是否非负。
还是通过经过哪个重心来划分路径,设当前重心为 \(rt\)。
设 \(f_i\) 表示在已经遍历完的子树里深度为 \(i\) 的点到根的最长距离, \(g_i\) 表示在当前子树里深度为 \(i\) 的点到根的最长距离。
显然用 \(\max\limits_{j \in [i - U + L,\ i]} f_{j} + g_{U - i}\) 去更新答案。
前面一项很容易用数据结构维护,但是加上点分治和二分复杂度有三个 log,无法通过。
发现 \(f\) 的答案区间是一个单调队列的形式,时间复杂度 \(O(\text{当前子树深度 + 之前出现过的最大子树深度})\)。
这样复杂度还是可能退化到 \(O(n^2)\),但是如果按深度从小到大枚举子树,统计答案这一步就是线性的。
如果直接点分治的同时去排序,仍然是三个 log 的。但是点分树的形态始终不变,我们可以在二分之前对于每个重心先排好序。
这样整体就是双 log 复杂度。submission
CF150E Freezing with Style
题意:给定一颗带边权的树,求一条边数在 \([L, R]\) 之间的路径,并使得路径上边权的中位数最大。输出一条可行路径的两个端点。
对于序列 \(a\),定义 \(b_i = \begin{cases}1& a_i \le \text{mid}\\ -1 & \text{otherwise}\end{cases}\),如果满足 \(\sum b_i > 0\),则 \(a\) 的中位数一定不大于 mid。
和上题做法一致,二分答案,如果存在路径满足 \(\sum b_i < 0\),说明答案还能变大。
设 \(\text{val}\) 为使所有合法路径 \(\sum b_i \ge 0\) 的最小值,那么所求答案即 \(\text{mid = val - 1}\) 时使 \(\sum b_i\) 最小的路径两端。
CF1260F Colored Tree
题意:
给定一棵树,每个节点有一个颜色 \(h\),\(h_i\) 为 \([l_i,r_i]\) 内的一个整数。
对于全部 \(M = \prod (r_i-l_i+1)\) 种不同的染色方案,求:
\(\text{dist}(i, j)\) 对答案的贡献有 $\big(d(i, rt) + d(j, rt)\big)\times \vert H_i\cap H_j\vert \times \prod_{k \ne i, j} (r_i - l_i + 1) $。
不妨只考虑交集中的某一点 \(x\) 对答案的贡献:
线段树维护交集贡献,时间复杂度 \(O(n \log^2 n)\)。
懒标记线段树爆了,考虑树状数组维护区间加区间和。
维护差分数组 \(b_i\),\(\sum a_i = \sum b_i \times (n - i + 1) = (n + 1) \times \sum b_i - \sum b_i \times i\)。
后面一个和式也用树状数组维护即可。submission
[HZOI 2015] 树黑白
题意:给定一棵树,一开始都是白色,支持两种操作:
- 将 \(u\) 反色。
- 查询 \(u\) 到所有黑色节点的距离和。
把 \(u\) 的路径按照经过哪个重心分成 \(O(\log n)\) 类。
因此不管修改还是查询都只要在点分树上暴力跳 \(\log n\) 次。
设 \(fa(x)\) 表示 \(x\) 在点分树上的父亲节点,定义 \(x\) 的子树为以 \(x\) 为分治中心时的子树。
设 \(f_x\) 表示 \(x\) 子树内的黑点到 \(x\) 的距离和,\(g_x\) 表示 \(x\) 子树内的黑点到 \(fa(x)\) 的距离和,\(cnt_x\) 表示 \(x\) 子树内的黑点数。
从 \(x\) 一直跳到点分树的根,假设当前点是 \(cur\),上一个点是 \(pre\)(\(fa(pre) = cur\)):
那么 \(cur\) 对询问 \(u\) 的贡献有
P2056 [ZJOI2007] 捉迷藏
题意:给定一棵树,初始全黑。支持单点改色,询问最远两个黑点距离。
类似直径求法,每个分治重心维护子树内黑点到 \(rt\) 的最大值与次大值,可删堆维护。
mutiset 常数过大了,手写一个懒删除堆。submission
P3345 [ZJOI2015] 幻想乡战略游戏
题意:给定一棵树,保证每个点度数不超过 \(20\)。单点修改点权 \(a_i\),每次查询最小的 \(f(u) = \sum_{v = 1}^n \text{dist}(u, v) \times a_v\)。
考虑暴力做法:
设当前在点 \(u\),\(\sum_u\) 表示 \(u\) 子树内的点权和,\(\sum = \sum_{i = 1}^n a_i\)。
与之直接相连的儿子 \(v\) 比 \(u\) 更优当且仅当 \(\text{dist}(u, v)\times (\sum - 2\sum_v) < 0\)(考虑答案的变化量)。
也就是 \(2 \sum_v > \sum\),如果存在儿子节点更优,那么只可能有一个 \(v\) 满足条件(\(2\sum_v > \sum \ge \sum_u\))。
- \(f(v) \ge f(u)\):
设 \(v'\) 为 \(v\) 子树中的一点。
由于 \(\sum\) 是常量,且 \(\sum_{v'} \le \sum_{v}\),因此从 \(u\) 走到 \(v'\) 只会使答案越来越大。
因此 \(v\) 的子树中不存在比 \(u\) 更优的点。
-
\(f(v) < f(u)\):
设 \(v'\) 为最优决策点。
显然 \(v'\) 在 \(v\) 的子树内,现在证明不管从 \(v\) 子树的哪个点 \(x\) 出发(将 \(f(x)\) 和 \(x\) 的儿子比较)都能到达 \(v'\)。
砍掉多余部分,只留 \(v'\) 的子树。
强制使 \(x\) 为根,由于 \(2\sum_{v'} > \sum\),一定有 \(2\sum_{fa(v')} > \sum\),因此存在一条 \(v'\) 到 \(x\) 的决策路径。
修改和单点查 \(f\) 可以用点分治轻而易举做到 \(O(\log n)\),瓶颈在与上述算法最坏会达到单次 \(O(n\log n)\)。
一个想法是枚举 \(x\) 的儿子 \(y\)(原树),如果 \(f(y) < f(x)\),说明最优决策点在 \(y\) 的分治子树中,递归到包含 \(y\) 的儿子(点分树)。
时间复杂度 \(O(20\log^2n)\),正确性可以由上述第二点得到。
加强版少去了度数限制,做法是链剖 + 线段树,待补。
P2495 [SDOI2011] 消耗战
题意:给定一棵树,\(m\) 次询问:给出 \(k\) 个关键点,求使每个点都不能到的最根小代价(花费 \(w(u, v)\) 使 \((u, v)\) 断掉)。
设关键点集为 \(S\)。
显然有 \(O(n)\) 的树形 dp,\(f_x\) 表示使 \(x\) 内关键点都不能到根的最小代价:
仅保留关键点之间的祖先后代关系(保留根节点,和关键点两两之间的 lca)不会改变最后的 dp 值。
定义虚树为包含关键点和所有 lca 的最小树。
以下构造方式能说明虚树规模只有 \(O(k)\),如果能快速建出虚树即可解决本题。
二次排序 + LCA 连边:
- 将关键点按 DFS 序排序;
- 遍历一遍,任意两个相邻的关键点求一下 LCA,并且判重;
- 然后根据原树中的祖先后代关系建树。
具体实现上,在关键点序列上枚举相邻的两个数,两两求得 LCA 并且加入序列 \(A\) 中。
此时 \(A\) 一定包含了虚树中的所有点(可能重复)。
假设 \(a, b, c\) 按照 dfs 序依次递增,则
\[\text{lca}(a, c) = \text{lca}\big(\text{lca}(a, b),\ \text{lca}(b, c)\big) \]由于 \(\text{lca}(a, b)\) 和 \(\text{lca}(b, c)\) 都是 \(b\) 的祖先,因此其中一个一定是另一个的祖先。
假设按照时间戳排序得到 \(x_1, x_2 \cdots x_m\)。
那么任意的两个节点的 lca 可以写做
\[\text{lca}(a_i, a_j) = \text{lca}\bigg(\text{lca}(a_i, a_{i + 1}),\ \text{lca}(a_{i + 1}, a_{i + 2}),\ \cdots,\ \text{lca}(a_{j - 1}, a_j) \bigg) \]任意两个关键点的 lca 都包含在我们新增的相邻 lca 里。
同时证明了虚树规模是 \(O(k)\) 的。
把 \(A\) 按照 dfs 序排序并去重。枚举相邻的两个点 \(x,y\),连接 \(\text{lca}(x, y)\) 和 \(y\),这样就完成了构造。
为什么这样连边不重不漏?
如果 \(x\) 是 \(y\) 的祖先。
由于 \(x, y\) 在 \(A\) 中相邻,因此 \(x\) 到 \(y\) 的链上不存在任何其他节点,直接 \(x\) 连向 \(y\) 即可。
如果 \(x\) 不是 \(y\) 的祖先。
\(\text{lca}\) 的 dfs 序一定小于 \(x\),因此 \(x\) 与 \(\text{lca}\) 在之前已经直接或间接相连了。
又由于 \(x, y\) 相邻,\(\text{lca}\) 到 \(y\) 之间不存在其他点,\(\text{lca}\) 连向 \(y\) 是不重不漏的。
P3320 [SDOI2015] 寻宝游戏
题意:每次新增或减少一个关键点,并询问包含所有关键点的生成子树的边权和 \(\times 2\)。
这颗生成子树的根一定是所有关键点的最近公共祖先,因为任意两点要求联通。
发现这与我们构建虚树的形式很像,可以单次 \(O(k\log n)\) 得到整棵树的边权和。
但是本题并不保证 \(\sum k\) 的量级。
Gym - 104128E
题意:给定一颗树,初始全白,问全部染黑的最小代价。把 \(x\) 子树里与 \(x\) 距离为 \(i\) 的点全部染黑代价为 \(a_i\)。
\(f(x, d)\) 表示在以 \(x\) 为根的子树里把深度为 \(d\) 的点全部染黑的最小代价,答案为 \(\sum f(root, *)\)。
对于每个深度建出虚树,时间复杂度 \(O(n\log n)\)。