点分治讲解
\color{white} 建议学习的部分前置知识。
点分治所在的知识体系
如图所示点分治属于树上分治的一种。
而树上分治指的是对树形结构的数据进行分治。
本次讲解主要针对点分治。
点分治的一些概念性的东西
- 本质:点分治实际上就是不断将一棵树拆成多颗子树进行数据处理,并不断进行。
- 过程:将一颗无根树变为有根树,然后递归处理每一颗存在根节点的儿子的子树。
- 优化&一些计算方法:
- 优化:以重心为根节点,可以证明这样的话递归深度最坏为
O _{(logN)} 。 - 计算方法:①(常用)每次走完一颗子树将有用信息存下来,然后在走下一颗子树的时候计算和记录的之前的信息贡献②先计算一个点所有子节点之间的贡献,再减去来自同一颗子树的部分
- 优化:以重心为根节点,可以证明这样的话递归深度最坏为
- 适用范围:对于一类统计树上路径信息(大规模
\color{white}~~,小规模一般暴力都能过吧……~~ )(比如距离\leq k 的点对数量,\mod m 意义下最长路径)等,可以考虑使用点分治。 - 效率:高效,能在
O_{(Nlog^{2}(N))} 的复杂度内查询所有路径信息。
点分治的大致框架
- 将树上路径分为两类,一类为经过根节点,一类为不经过根节点。
- 第一类是核心部分,该部分统计方法因题而异。
- 第二类则是将根节点的子树作为新的树递归下去计算。(可以理解为删除根节点,在剩下的所有子树上重新寻找合适的根节点并进行第一类的计算后,重复第二类操作)
关于树的重心:贴心的老师已经为不清楚的同学准备好了讲解文案,我们一起来阅读一下。
传送门
例题1:P4178 Tree
题目大意:
定一棵
再给定一个正整数
解析:
题中的
我们将重心作为树的根节点,将其变为有根树(至于为什么将重心作为根节点,原因在下面解释)。
对于存在的路径,将路径拆分为多段,每段路径将不在根节点的同一颗子树上。这样才能正确计算距离,不然在同颗子树上会出现路径重合从而得出错误路径。
对于拆开的路径,我们可以用
用
于是,对于两个节点
-
b_{x}{=}\mathllap{/\,}b_{y} -
d_{x}+d_{y} \leq K
对于需要求的核心第一类路径,也可以使用大减小的方式,由于不实用,不做赘述(唯一的优点是数组用得少)。该方法代码在这。
实际程序有两种常见方式 (注意前面提及的路径计算多种方式与此多种程序实现方式不同,路径计算方式只是某些细节上的区别,是基于程序实现上的细节):
方法一:树上直接统计
容我搬运一发老师的讲解谢谢
设根节点
可以建立一个树状数组,一次处理每棵子树
1、对
2、对
上述步骤先处理第1步(一棵子树处理完),然后再处理第2步,保证节点对不在同一子树上。
值得注意的是,树状数组的范围和路径长度相关,这个数值如果较大的话,就会让时间、空间复杂度容易超出范围,且距离不容易离散化,较难优化。虽然可以用其他结构(例如平衡树)来保证复杂度,但代码复杂度就会明显增加。
方法二:指针扫描数组
把树中的每个点放进一个数组
那么,指针
用数组
显然以上两种方式虽然不同,但是点分治过程都大差不差。
- 找到根节点
root - 从
root 出发进行Dfs 求出数组d 和b 。 - 执行
solve(root) - 删除根节点
root ,并对其每颗子树进行点分治。
代码
徐老师的代码见文件,至于我的代码上文已经提供链接。
使用重心之因
开头提到过重心来优化时间,这里详细阐述一下将重心作为根节点进行优化的原因。整个时间复杂度为
例题2:P3806 【模板】点分治1
题目大意:
给定一棵有
存在输出 AYE,否则输出 NAY。
解析:
与上一题相似,但是本题要求的是判断是否存在距离为
代码:这里
练习
习题:AcWing264、AcWing3014
洛谷题号:P4149、P5563、P2993