点分治
额,就是你每次去找一棵树的重心,然后将这棵子树变成以这个重心为根的树,再在这个树上进行某些操作,就可以在 \(O(n\log n)\) 的时间复杂度遍历到任意两个点 \(u,v\) 在 \(P(u,v)\) 上某个点 \(x\) 为根时候的贡献了。那么对于类似于求点对 \((u,v)\) 的某些价值和时,就可以上点分治。差不多吧,没啥用。然后可以去做河童重工。
点分树
点分树相当于动态点分治。对于每次找到的一个重心 \(x\),我们去 \(x\) 为根的子树中又找到了若干个 \(x\) 的儿子为根的子树中的重心 \(v_1\sim v_k\),那么在点分树上就有:\(fa_{v_i(1 \le i \le k)}=x\)。那么现在这棵点分树上任意一个点 \(u\),都有 \(dep_u \le \log n\),那么还有这棵树上所有节点的 \(siz\) 之和是 \(n\log n\) 级别的。性质优美,所以暴力。
那么对于点对 \((u,v)\) 贡献和的问题,我们就可以考虑枚举 \(u\),然后枚举 \(\operatorname{LCA}(u,v)=x\),维护 \(y \in son_x \land \operatorname{LCA}(y,u)\ne y\) 的子树中所有节点的价值,和 \(u\) 一起计算一下贡献。
来点例题/练习题。
P6329
题意是说支持动态修改点权,求 \(\sum \limits_{y=1}^{n} val_y [dis_{x,y}\le k]\)。
我们把点分树建出来。先不考虑点权的修改,考虑如何维护 \(x\) 已知时的答案。我们去枚举 \(\operatorname{LCA}(x,y)=u\),记 \(f_{u,k}=\sum\limits_{v=1}^{n}val_v [\operatorname{LCA}(u,v)=u\land dis_{u,v}+1 \le k],g_{u,k}=\sum\limits_{v=1}^{n}val_v [\operatorname{LCA}(u,v)=u\land dis_{u,v}\le k]\)。那么对于当前枚举到的一个 \(u\),假设我们是从 \(v\) 跳 \(father\) 上来的,那么有贡献为:\(g_{u,k-len}-f_{v,k-len}\),其中 \(len\) 为 \(x\) 到 \(u\) 的距离。
那么我们只要维护出来 \(f_{u,k}\) 和 \(g_{u,k}\) 就行了。你会发现,\(f_{u,k}=g_{u,k-1}\),然后贡献就行 \(g_{u,k-len}-g_{v,k-len-1}\)。对于一个点 \(u\) 的点权变成 \(val\),它会给到贡献的点 \(v\) 一定是 \(u\) 的祖先。我们去枚举 \(v\),已知了一个路径长度 \(len\),那么 \(k \ge len\) 的都有:\(f_{v,k} \to f_{v,k}+val\)。
然后就做完了,可以任意维护 \(f_{i,j}\)。比如对每个 \(i\) 开一棵动态开点线段树。由于每次修改只会更新 \(O(\log n)\) 个节点,每个节点是一个区间加,那么单次修改会最多新增 \(O(\log^2 n)\) 个点。总的点数是 \(O(n\log^2 n)\) 的,时间复杂度 \(O(n\log^2 n)\)。
P10603
就是把 P6329 操作反过来。对于枚举的 \(\operatorname{LCA}(x,y)=u\),每次相当于是 \(O(1)\) 次 dfn序区间上 \(dep_v\) 不超过某个值的修改。注意到我们的询问是单点查询,所以可以考虑直接在点 \(u\) 上面加个 tag,表示 \(dep_v\) 不超过某个值的所有点会增加某个值。然后单点查询的时候暴力枚举 \(\operatorname{LCA}\) 算下贡献和就行了。时间复杂度 \(O(n\log^2 n)\)。
P10604
求 \(dis_{x,y}\) 的第 \(k\) 小值。那么当 \(\sum\limits_{i=1}^{n}[dis_{x,y} \le K] <k\) 时,说明第 \(k\) 小值比 \(K\) 大。然后二分即可,时间复杂度 \(O(n\log^3 n)\)。