[JZOJ5776]【NOIP2008模拟】小x游世界树
考虑不换根怎么做, 那么每条边经过的次数就是它的子树大小。
如果换根, 那么考虑root的儿子x, 它的答案中只有root到x的边的贡献发生变化了。
发生的变化是减去root到x的贡献, 加上x到root的贡献。
于是求出root为1的答案, 然后再换根, 这样统计答案是O(1)的, 总共是O(N)的。
注意更新的顺序一定要按照dfs顺序, 还要开Long long.
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #define int long long using namespace std; inline int read() { int res=0;char c=getchar();bool f=0; while(!isdigit(c)) {if(c=='-')f=1;c=getchar();} while(isdigit(c))res=(res<<3)+(res<<1)+(c^48),c=getchar(); return f?-res:res; } #define reg register #define N 700005 int n; struct edge { int nxt, to, val; }ed[N*2]; int head[N], cnt; int ine[N]; inline void add(int x, int y, int z) { ed[++cnt] = (edge){head[x], y, z}; head[x] = cnt; } int a[N], siz[N], fat[N]; int ans, root = 1, res; int f[N]; void dfs(int x, int fa) { siz[x] = 1; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; fat[to] = x; ine[to] = i; dfs(to, x); siz[x] += siz[to]; } } int efs(int x, int fa, int in) { int sum = 0; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; sum += efs(to, x, i); } sum += siz[x] * (ed[in].val - a[fa]); return sum; } void ffs(int x, int fa, int in) { if (x != 1) f[x] = f[fa] - (siz[x] * (ed[in].val - a[fa])) + (n - siz[x]) * (ed[in].val - a[x]); for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; ffs(to, x, i); } } signed main() { freopen("yggdrasil.in", "r", stdin); freopen("yggdrasil.out", "w", stdout); n = read(); for (reg int i = 1 ; i <= n ; i ++) a[i] = read(); for (reg int i = 1 ; i < n ; i ++) { int x = read(), y = read(), z = read(); add(x, y, z);add(y, x, z); } dfs(1, 0); res = efs(1, 0, 0); ans = res; f[1] = ans; ffs(1, 0, 0); for (reg int i = 2 ; i <= n ; i ++) if (f[i] < ans) {ans = f[i], root = i;} printf("%lld\n%lld\n", root, ans); return 0; }