树点分治--树上的重心
Updated:
引言
在面对一个无根树的时候,我们往往无从下手,便随便挑选一个节点作为根节点进行操作。
这是很不负责的一种行为,因为它可能导致很高的复杂度。比如对于一条链,你选择了两头的结点。。你很有可能面对爆栈的风险(NOIP2016D1T2 = =亲身经历)
那么,我们如果把一个很好看的树,比如一个满二叉树,把这个图看成无根图。
你会发现满二叉树的原本的根节点就是最好的选择:它恰巧在整个图的“中央”。
我们把这种最好的根节点的选择称为树的重心或者树的质点。
树的重心:
那么我们如何去求一棵无根树的重心呢?
当然是从重心的性质入手啦!
我们可以认识到,树的重心其实就是在整个图的“中央”,那么这个中央如何进行定义呢?为了保证深度最小(如果你把根节点放在树的重心上,那么它深度当然是最小的啦!)我们可以把深度最小近似看成是子树结点最少。(二者的关系的密切程度随着子树的划分逐渐密切)
即:去掉重心后,使得剩余子树的最多结点数最小。
如何实现?
我们需要保存一个子树最大结点数最小的情况,可以压缩为一个值min
min代表我们找到的最小值,对应一个变量cog为树的重心的编号
现在就是遍历所有的情况了,那么有人心态崩了:我岂不是要对所有的结点来一遍dfs??
但其实不是这样的,我们只需要一次dfs,如果按照楼上的说法,我们解决了太多的重复子问题了。
我们任选一个结点作为本次寻找树的重心的根节点,然后进行一个dfs,我们意识到dfs可以遍历到所有的结点,也就是可以求到所有的划分子树的值。
怎么获取呢?
我们假设当前节点是now,父亲结点是from,now的儿子们是v[]
那么我们可以递归求得v[i]中包含的结点数,而now本身的返回值是sum=v[1]+v[2]+…+v[n]+1
此时我们遗漏了一个子树:那就是以from为根节点的子树!
而这个子树的节点数有点好求:N-sum
N为总结点数,sum为所有儿子的节点数+1
于是我们对比所有的值,取出最大值max
将max与min比较,更新cog
一遍遍历以后我们便取到了树的重心。
代码!(这次我真的不想写注释了)
1 |
|