初识点剖(基于点的树上分治)

来源:互联网 发布:java如何实现线程同步 编辑:程序博客网 时间:2024/06/05 18:33

有一类问题,是关于树上路径,树上点对,树上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,常数略大

2 0
原创粉丝点击