一种利用分治进行树上路径统计的算法,算法时间复杂度与维护经过根结点路径相关信息所需的时间复杂度有关,一般为$O(n\log n)$
所有路径分为两种,一种是过根结点的,一种是完全在根结点的某棵子树中的。因此可以考虑分治算法,选取一个点将无根树转换为有根树,然后递归处理每个棵以根节点的儿子为根的子树。如果选取树的重心作为根结点,则每棵子树的结点个数不超过$\frac n2$,可以保证递归深度不超过$\log n$
在这个基础上如果能在$O\left(n\right)$时间维护经过根结点路径相关信息,则算法总时间复杂度为$O(n\log n)$
重心的寻找可以利用树形dp,处理出所有结点的$\text{sz}$,所有结点的最大子树 $\text{mson}\left(u\right)=\max\left(\max\left(\text{sz}\left(\text{son}\left(u\right)\right),\text{tot_sz}-\text{sz}\left(u\right)\right)\right)$,不断更新$\text{mson}$最小的结点,最后便可以得到重心,时间复杂度$O(n)$
分治过程需要注意标记已经访问的结点,同时更新子树大小$\text{tot_sz}$,具体实现见模板
题目大意是说给定一棵$n$个结点的正权树,多次查询,每次查询是否存在长度为$k$的路径
对根结点,先考虑暴力法,用树形dp求出子树上各节点到根结点的距离,然后两两枚举,看看有没有两个结点距离和为$k$,这样每层递归的时间复杂度是$O\left(n^2\right)$,显然会T
考虑用中途相遇法,可以将每层递归的时间复杂度优化到$O(n)$,单次查询时间复杂度$O(n\log n)$
但要注意三点
一、枚举过根结点的路径时路径两端点显然不能在同一棵子树里,所以要先求出一棵子树所有的$\text{dist}$,全部判断完后再进行标记
二、题目给定$k\le 10^7$,所以不用标记大于$10^7$的$\text{dist}$
三、清空标记不能用$\text{memset}$,会T,这里开了一个$\text{vector}$来记录之前的标记
另一个优化是可以离线处理查询,这样只需要分治一次,可以减小常数。
给一棵树,每条边有权。求一条简单路径,权值和等于$q$,且边的数量最小。
类似习题1,开个$\text{mark}$数组存一下到根结点距离为$\text{dist}$的路径的最短边数。$\text{vector}$不仅要记录距离,还要记录深度,时间复杂度$O(n\log n)$
给定一棵$n$个结点的正权树和一个正数$k$,统计有多少对结点$(a,b)$满足$\text{dist}(a,b)\le k$
把中途相遇法换成树状数组或名次树即可,时间复杂度$O\left(n\log^2 n\right)$
给定一棵$n$个结点的树,每条边包含长度$L$和费用$D(1\le D,L \le 1000$)。选择一条总费用不超过$m$的路径,使得路径总长度最大。
考虑单调队列,时间复杂度$O\left(n\log^2 n\right)$。另外本题存在$O\left(n\log n\right)$解法,有兴趣的可以自己尝试
给定一棵$n$个结点的树,树的每个节点有个颜色
定义$s(i,j)$为$i$到$j$的颜色数量,$sum_i=\sum_{j=1}^n s(i,j)$,要求输出所有$sum_i$
这题需要计算贡献,思路较复杂,这里只给出代码供参考,时间复杂度$O(n\log n)$,另外本题存在$O(n)$解法,有兴趣的可以自己尝试