题意:给出一个n(<=1e4)个点的树,每条边有权,求树上长度小于等于k的路径条数。(u,v)和(v,u)算两条。
点分治:顾名思义,点分治就是对点进行分治,一般用于路经统计问题。对每个点而言,一条路径要么经过他,要么不经过他,这就是分,即分成路径经过此点和不经过此点。基本思想是:当经过某点的路径可以比较方便快速统计的情况下,通过对点进行分治,把树的规模不断变小。
考虑这样做的好处:把树看作无根树,除了叶子结点外,去掉任意一个点,都可以把一棵树分裂成若干规模更小的树。然后先统计好经过此点的路径,然后只需要统计不经过此点的路径,那么就等价于把这个点删掉,所以只要复杂度合理,点分治是普遍适用的。
关于点的选取:考虑最坏情况:一条链,如果顺序选取,可能会出现每次都选了链的端点,那么点分治本来是想通过对点进行分治,把一棵大树,分解成几棵小树,分而治之。那么如果随意选点,很可能对点分治之后,分解出的树规模和刚刚的差不多,导致效率极低。
树的重心:一棵无根树,对于某个点,把他去掉之后,原树会分裂成几棵小树,每个小树有若干个节点。树的重心就是一个点,去掉他之后,裂开后得到的最大的小树规模最小,显然,点分治中,对树的重心进行分治是最好的。他可以稳定的把树的规模不断变小。
一般套路:对整个树(联通快),求出一个重心,对重心这个点进行分治,先统计出经过这个点的路径。然后把这个点删掉。把整个树分解成小树(联通快)。对每个小树(联通快)继续求重心,继续进行分治。
题解:考虑某个点x,经过x且长度小于等于k的路径个数可以很方便求出:把树变成x为根的树,对每个点求出x到他的距离,然后双指针进行组合。但是有一个后果:某个经过x的路径可以分成两段,这两段必须分布在两个子树上,如果在一个子树上,他们的LCA不等于x,也就是不会经过x,上面统计完成之后,还要对每个子树进行去重,文末的代码更容易理解。想清楚经过某点的路径如何统计之后,那么就可以按照上面点分治的套路搞了。
个人感觉点分治就是