果然点分治这种东西跑不过树剖啊(
首先,有一个结论:最优的补给站的位置就是树的重心,不过原来的子树大小要换成子树点权和。
证明:假设最优解是一个在重心以外的点 x,令其向重心移动一步。设走过的这条边边权为 w,会走到 y。
那么会发现在 y 那边的点的贡献全部少了 w,在 x 这边的点的贡献全部多了 w。
由于 x 不是重心,所以 y 那边的点权和一定大于 x 这边的点权和。
故越接近重心解就更优,最优解即重心。
求重心比较简单,根据重心的性质,其实重心就是满足 2sizex≥sizeroot 的最深的 x。其中 root 表示树根。
在线段树上根据 DFS 序维护 size,然后直接线段树上二分找最深的即可。
用 DFS 序是因为 DFS 序越大就越深(当然是同一条链上)。
设重心为 u,那么问题就变成了求 n∑v=1dv⋅dist(u,v)。
这个其实也挺套路的,首先把路径转化为到根相减,然后分别写成三个和式,得 n∑v=1dv⋅dist(root,u)+n∑v=1dv⋅dist(root,v)−2n∑v=1dv⋅dist(root,lca(u,v))
前两个和式很方便维护,重点在于第三个。
考虑 u 到根路径上的一个点 x 到其一个儿子 y 的一条边权为 w 的边,显然它会影响 y 子树内的所有点的贡献,即 w⋅sizey。因为这些点和 u 的 lca 到根的路径必然会经过这条边。
于是令 wp 表示 p 到其父亲的边权,最后一个和式其实就是对 u 到根路径上的每一个点 x,求出 sizex⋅wx 之和。
修改时需要改动 size,实际上改动的就是该点到根的路径上的 size。
线段树和树剖维护即可。
代码:
1 |
|