点分树(动态点分治)学习笔记

1.0 定义

其实本质也是一种暴力。。

回忆点分治的过程:每次找到当前子树的重心,处理所有经过该重心的路径的答案,然后将其删去,分裂成一些子树,再分别进去递归。

把点分治的过程离线下来,将当前树的重心与上一层的树的重心连边,这样就可以得到一棵树,我们称之为“点分树”。

1.1 应用范围

点分治通常针对树上路径问题,一般是”任意两点间的路径“。还可以处理树上可二分型问题,使用点分治优化一步步走的过程。(如树的重心,例:P3345

但有时会加上多次询问(并强制在线等),不可能每次都跑一遍点分治。于是我们建出点分树,每次在点分树上求解即可。

1.2 性质

点分树有两个性质:

  • 点分树的树高是logn级别的(每次找重心,size至少减半)。这是一个强有力的性质,我们还可以据此推出点分树上的szinlogn级别的(证明考虑每个点最多对祖先贡献logn次)。所以我们可以给每个点开一个包含子树中所有点的vector(或者开到dep),这样做空间也是能接受的。并且修改操作也可以考虑直接暴跳祖先(总之就是暴力搞)。
  • 对于任意两点x,y,可以确定的是x,y在点分树上的lca一定在(原树上)uv的路径上。这点比较重要,因为据此有dis(x,y)=dis(x,lca)+dis(y,lca),有时可以转化问题。

1.3 求法

贴一份代码(其实就比点分治过程多了一句话):

void pcalc(int x, int f)
{
	sz[x] = 1;
	for (auto y : g[x]) if (y != f && (!vis[y])) pcalc(y, x), sz[x] += sz[y];
	return;
}

void solve(int x)
{
	vis[x] = 1; pcalc(x, 0);
	for (auto y : g[x])
	{
		if (vis[y]) continue;
		rt = 0, ns = sz[y]; findrt(y, x); 
      	        nf[rt] = x; solve(rt);
	}
	return;
}

1.4 例题

点分树 | 震波

借这道题说一说点分树的一般套路。

按照上面的性质,把所有的y按照和x(在点分树上)的lca分类(最多有logn种),有

res=dis(x,y)kay=dis(x,z)+dis(z,y)klca(x,y)=zay=dis(z,y)kdis(x,z)LCA(x,y)=zay

满足lca(x,y)=zy即为z的子树抠掉z 在 x 方向上的儿子 s 的子树后剩下的部分。所以答案即为 z的子树内到 z的距离kdis(x,z) 的点权和减去s子树内到 s 的距离kdis(x,z) 的点权和(容斥思想)。给每个点开一个树状数组,下标为i的位置维护 x 子树内所有 dis(x,y)=i 的ay 的和,从而下标只要开到maxdep范围,防止MLE

特别注意,除了维护上述贡献外,还要维护xfax的贡献。切记不能只维护一个,然后在 s对应的线段树上查 kdis(x,z)1的和,因为点分树上的xfax基本没有关系。

1.5 总结

基本上是对每个点x维护它子树内的所有点对它的贡献和它们对fax的贡献(用来去重)。基本都要带上一些数据结构,复杂度一般是O(nlog2n)

posted @   andysj  阅读(190)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示