一、题目
二、解法
本来以为这个题就是个垃圾题,结果是我没理解到,我是傻逼。
首先你看到这种最小化距离的题一定要想到重心,这道题其实也是重心鸭。你把 d u d_u du 看成节点 u u u 上的真点个数,那么取这些真点在图上面的重心就是最小化的距离。
由于最近在学点分树,就想到了用点分树的角度来思考这道题。这里有两个关键的条件:点分树的树高是 log n \log n logn 级别的,点分树的度数也是 20 20 20(相当于 log n \log n logn 级别的)
那么就想到在点分树上暴力找,不知道在哪里,那就从根开始,由于真点的重心只有一个,所以如果我们要移动当前的答案那么至多只会走向一个分治子树,这就是复杂度的保证。
判断重心在哪个子树的方法可以推柿子,但是那东西有点难维护,这里又涉及到了对点分治的理解。他所能维护的是管辖范围内的点而不是原树上的点,所以有些东西是不能维护的。
介绍一种暴力判断的方法,你找到分治子树在原树上直接相邻的节点考虑向他移动,如果答案更优那么重心一定在这个分治子树内,我们至多只会移动到一个子树(因为至多只有一个方向答案是减少的),现在你一定会点分治有了更深的理解,所谓分治子树只是保证复杂度的一种方式,而不要把他当作原树一般看待。
统计答案是有手就行的,做过板题震波
的同学都知道我们要维护分治子树内的点到当前点的总贡献 s [ u ] s[u] s[u],分治子树内的点到 u u u 的分治父亲的总贡献 f s [ u ] fs[u] fs[u],再维护分治子树内的真点数 s d [ u ] sd[u] sd[u],那么假设算的是 x x x 的答案,要把 ( x , i ) (x,i) (x,i) 的距离考虑进去就行了( i i i 是当前跳到的结点)
时间复杂度 O ( n log 3 n ) O(n\log ^3n) O(nlog3n),点分树的代码都很好写鸭。
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M =