算法概述
点分治用来统计树上满足某种条件的路径个数,一般具有可合并答案的性质(比如是否存在路径长度 $=k$等)。
算法步骤
首先我们考虑经过根节点的满足条件的路径有多少。那比如我们就拿长度为 $k$ 举例,先从根节点 $DFS$ 一遍,求出每个节点的 $dis$。然后对于一个节点的 $dis[i]$,统计 $k-dis[i]$ 是否存在即可。如果存在,那么就有。
当然,并不是所有的路径都经过根节点。那么,不经过根节点的路径,一定经过根节点子树中的某个点。
那么我们把根节点的每个子树都分治一遍(递归),不就能求出答案了吗?
但是我们发现这样太慢了,复杂度为 $O(n\times C)$,其中 $C$ 为递归的次数。但是很显然,树为一条链的时候,$C=n$,复杂度变成 $O(n^2)$。
如何优化这个算法呢?也很简单,我们都有 $DFS$ 一遍的时间,顺便把每颗子树的重心都求出来好了。
然后对于每个子树,我们递归这个子树的时候,以重心为根。
这样,容易证明,递归次数 $C\le\log n$。
模板题
洛谷 3806:给一颗树,多组询问是否存在长度为 $k$ 的路径。
$n\le 10^4,m\le 100$
解法:每次点分治的时候,维护一个 $bool$ 数组 $cxk$,$cxk[i]$ 表示是否存在 $dis[k]=i$。分治的时候,遍历每个询问 $k$,如果 $cxk[k-dis[i]]$,那么这个询问答案变成 $true$ (题目要求输出 AYE
)
总的复杂度是 $O(nm\log n)$
模板题代码
1 |
|