题解:
先考虑一个简单的问题:怎么在有修改的情况下快速求一个点作为补给站的答案。
注意到和路径有关,所以很容易想到点分树。
接着可以想到一个十分暴力的做法,就是修改后,每次枚举相邻的点,如果更优就走过去。
很容易想到因为每次重心不会移动太远,这个是可以过的,数据也不太好造来卡这个做法。
那么有没有稳定的做法?
还是每次枚举子树,假设现在在点x,枚举的子树是y,发现y的答案比x优,那么就走到y子树的(不带权)重心去,这样相当于在点分树上逼近。
这个做法巧妙之处在于利用点分树的优美结构。
复杂度是 O ( n l o g 2 n ∗ 20 ) O(n~log^2~n*20) O(n log2 n∗20)
Code:
#include<cstdio>
#define max(a, b) ((a) > (b) ? (a) : (b))
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 1e5 + 5;
int n, x, y, z, Q;
int fi[N], nt[N * 2], to[N * 2], v[N * 2], tot;
void link(int x, int y, int z) {
nt[++ tot] = fi[x], to[tot] = y, v[tot] = z, fi[x] = tot;
}
int siz[N], mx[N], G, bz[N];
void fg(int x) {
bz[x] = 1; siz[x] = 1; mx[x] = 0;
for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]])
fg(to[i]), siz[x] += siz[to[i]], mx[x] = max(mx[x], siz[to[i]]);
mx[x] = max(mx[x], siz[0] - siz[x]);
G = mx[G] < mx[x] ? G : x;
bz[x] = 0;
}
int fa[N], dep[N], dis[19][N], D, bl[19][N];
void dfs(int x) {
bz[x] = 1;
for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]])
dis[D][to[i]] = dis[D][x] + v[i], dfs(to[i]);
bz[x] = 0;
}
void dg(int x) {
fg(x); D = dep[x]; dfs(x); bz[x] = 1;
for(int i = fi[x]; i; i = nt[i]) if(!bz[to[i]]) {
siz[0] = siz[to[i]], G = 0, fg(to[i]);
fa[G] = x, dep[G] = dep[x] + 1, bl[dep[G]][to[i]] = G;
dg(G);
}
}
struct nod {
ll x, y;
} a[N], b[N];
void add(int x, ll c) {
a[x].y += c;
for(int p = x; p; p = fa[p]) {
int k = dep[p] - 1;
if(fa[p]) {
a[fa[p]].x += c * dis[k][x], a[fa[p]].y += c;
b[p].x += c * dis[k][x], b[p].y += c;
}
}
}
ll fd(int x) {
ll s = 0;
for(int p = x; p; p = fa[p]) {
int k = dep[p];
s += a[p].x + a[p].y * dis[k][x];
if(fa[p]) s -= b[p].x + b[p].y * dis[k - 1][x];
}
return s;
}
int g;
int main() {
scanf("%d %d", &n, &Q);
fo(i, 1, n - 1) {
scanf("%d %d %d", &x, &y, &z);
link(x, y, z); link(y, x, z);
}
siz[0] = mx[0] = n; fg(1); int g0 = G; dg(G);
fo(ii, 1, Q) {
scanf("%d %d", &x, &y);
add(x, y); g = g0;
while(1) {
ll s = fd(g); int c = 1;
for(int i = fi[g]; i; i = nt[i]) {
if(dep[to[i]] < dep[g]) continue;
int y = bl[dep[g] + 1][to[i]];
if(fd(to[i]) < s) {g = y; c = 0; continue;}
}
if(c) break;
}
printf("%lld\n", fd(g));
}
}