标签:log 倍增 判断 com 完成 soa 树状数组 图论 超过
本题解是题解栏内一些常见思路的集合。
为了篇幅紧凑,在一些地方我可能跳过了证明/阐述的不是怎么详细,如果希望看到某一个思路的详细阐述/代码,可以点击相关的超链接。
本题的部分分启发我们去找性质:
性质:
计数方法:
考虑一个点\(u\),我们想要知道,它会成为几次重心。
将树在\(u\)处定根,设其最大的子树大小为\(S=siz_w\),则可以分为两种情况:
因此,我们考虑处理出以\(u\)为根,各个儿子的\(\{siz\}\)的可重集合的情况,再查询一段区间内的\(siz\)的个数即可。
可以用可持久化线段树(可能可以线段树合并/将所有查询离线化)维护。
特别地,\(fa_u\)的情况,相当于\(tree-anc_u-tree_u\)与\(\{u\}+anc_u-\{rt\}\)取反。(其中,\(anc_u\)表示\(u\)的所有祖先节点)
我们先找到重心\(rt\),并以它作为根。这样和随意选根有什么区别呢?
性质:若\(x\ne rt\)为重心,则删去的边\((u,v)\)一定不在\(tree_x\)内。
因此,对一个点\(u\ne rt\),他作为重心仅当删去了一条边\((x,y)\),且:\((x,y)\)不在\(tree_u\)内,\(n-2s_x\le S\le n-2g_x\)。(设树减少的大小为\(S\))
对于根的情况,可以另外(分成割去的边在重儿子子树内与不在重儿子子树内)判断。
可以用树状数组维护,具体见这篇题解。
考虑一种删边情况,我们需要快速求出,划分后的所有重心。
考虑如何求一棵树的重心:因为重心一定在根节点所在重链上,从根一直跳重儿子,直到找到最深的\(v\)使得,\(2s_v > s_u\),则\(v\)(可能还有它的重儿子)为重心。此过程可以用倍增加速。
由这种做法,我们可以在树链剖分,并预处理倍增数组之后\(O(\log n)\)地求出任何子树的重心。
我们考虑一个划分\((u,v)\),不妨设\(dep_u>dep_v\):
考虑如何求\(tree_u\)的重心:我们从\(u\)出发,一直向重儿子跳,跳到找到重心(即,重儿子的子树大小不超过原树的一半)为止。
考虑如何求出\(tree-tree_u\)的重心:我们先将根换到\(v\)处(此时只改变了两个节点的关系),这样实际上就相当于求\(tree_u\)的重心!
具体实现在此。
就如之前所说的,可以在\(O(n)\)计算出所有子树的重心,但是很难对去掉子树的部分找到一个重心单调移动的计算序列……
以重心为根,考虑删去的边在哪棵子树内:
这样就用纯图论方法完成了这题。
标签:log 倍增 判断 com 完成 soa 树状数组 图论 超过
原文地址:https://www.cnblogs.com/topsecret/p/12606466.html