有一类问题,是关于树上路径,树上点对,树上XX…之类的,我们可以考虑运用分治算法.
Description
给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K
Input
N(n<=40000) 接下来n-1行边描述边,按照题目中写的输入 接下来是k
Output
一行,有多少对点之间的距离小于等于k
如果说维护的是路径,那么这条路径有三种可能:
1. 在某个子树里
2. 通过根节点到达另外一颗子树(包括停留在根节点)
设 disi 为第i个节点到根节点的距离, chirooti 为第i个节点属于哪一个当前根节点的子节点下的子树.
那我们需要求解的问题,就转换成了以下两个问题
问题一: 求满足
disi+disj<=k
,且
chirooti!=chirootj
有多少对i,j的问题 (不同子树)
问题二: 求满足
disi+disj<=k
,且
chirooti=chirootj
(同一子树)
但我们注意到,若在同一子树下有可能存在
lca(i,j)!=根节点
的情况,即其路径多算了
dislca(i,j)
.
那么,我们递归地考虑,在这种情况必定发生在同一子树中,那么每当递归到一颗新树,就用其
满足
disi+disj<=k
有多少对i,j
的结果减去
满足
disi+disj<=k
,且
chirooti=chirootj
的结果,那么得出来的所有点对是绝对符合题意的,因为他们必定不在同一子树内.
剩下符合题意却没有计算的点,会在子树中递归计算.
若是无序点对则需要多添加一个i < j的条件
若该树是一条链,那我们需要做N次递归,每次计算答案是N log n(排序),即需要 n^2 log n级别的时间复杂度
所以,每一次用O(n)的时间找到树的重心(其实就是找到一个将树分的最平均的点,即最大子树节点数目不超过n/2)
然后再进行递归求解,每次计算一颗有N个节点的树的时间就是(n log n + k(n/k log n/k)…..) (K叉树)
n/k实际上是会越变越小,他收敛很快,所以大约就是n log n,常数略大