「ZJOI2015」幻想乡战略游戏
先考虑怎么高效地找到重心。
我们从根节点(令其为 )开始,每次往一个儿子走,要求以这个儿子为中心的的距离和更小(把式子写出来,容易知道这样的儿子要么没有,要么唯一),如果没有这样的儿子就结束。发现这个东西其实跟边权没有关系,因为写出来是一个 的形式( 是子树点权和),我们只关注它的正负,所以和边权无关。所以走的条件就是找一个满足 的儿子 。
怎么做呢?相当于找到 dfs 序最大的 使得 。把 放在线段树上 dfs 序对应位置维护,一个节点维护区间 最大值,查询的时候直接线段树上二分即可。
找到重心后,求答案是显然的,每一条边的贡献是边权乘上子树内/外点权和,用两棵线段树维护,一棵专门维护边权乘以子树内和,另一棵维护边权乘以子树外和。相当于维护区间 和 ,操作是 的区间加,以及求区间 ,是很经典的。
至此我们用 的时间复杂度解决了本题。
关于寻找重心,tx344 还阐述了一个更有意思(赤石,但是拓展性更强)的算法。
我们令 表示 dfs 序为 的节点编号,那么我们按如下步骤构造一个序列:先写下 个 ,然后 个 ,如此一直写到 个 ,观察发现这个序列的中位数(如果长度为偶数,则取左右两个都可)在重心的子树内。再从这个点出发向上二分。这个做法的优势在于,可以进行点权的子树加。如果使用一开始的方法,有两种截然不同的加法形式,可以求和,但是不便于求最值。对于这种方法,虽然还是线段树上二分,但我们要维护的是和,而非最值,所以把两个加法操作直接拼起来是没问题的,例题是 QOJ3307。时间复杂度与第一种方法是一样的。
本文作者:TulipeNoire
本文链接:https://www.cnblogs.com/TulipeNoire/p/18710586/P3345
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步