因为是英文题,题目不再重复。。
题意
给你一棵无根树,每条边有边权,每个点有点权,要你选一个点,使每个点到这个点的距离*点权的和最小,求这个值。
思路
设dis[u]为u所有后代到它的距离*点权,sum[u]为u所有后代的点权和包括u
先以1为根Dfs一遍,预处理出所有的dis,sum
然后问题就变成了更换这棵树的根,维护dis,取所有根的dis的最小值。
所以再一次Dfs,从u转移到儿子v时,dis = dis[u] - f(边权) * sum[v] + f * (sum[u] - sum[v])
常数巨大的丑陋代码
# include <stdio.h>
# include <stdlib.h>
# include <iostream>
# include <string.h>
# include <math.h>
using namespace std;
# define IL inline
# define RG register
# define UN unsigned
# define ll long long
# define rep(i, a, b) for(RG int i = a; i <= b; i++)
# define per(i, a, b) for(RG int i = b; i >= a; i--)
# define uev(e, u) for(RG int e = ft[u]; e != -1; e = edge[e].nt)
# define mem(a, b) memset(a, b, sizeof(a))
# define max(a, b) ((a) > (b)) ? (a) : (b)
# define min(a, b) ((a) < (b)) ? (a) : (b)
IL int Get(){
RG char c = '!'; RG int num = 0, z = 1;
while(c != '-' && (c > '9' || c < '0')) c = getchar();
if(c == '-') z = -1, c = getchar();
while(c >= '0' && c <= '9') num = num * 10 + c - '0', c = getchar();
return num * z;
}
const int MAXN = 100001, INF = 2147483647;
struct Edge{
int to, nt, f;
} edge[MAXN << 1];
int n, cnt, ft[MAXN], w[MAXN];
ll dis[MAXN], ans = 1e18, sum[MAXN];
IL void Add(RG int u, RG int v, RG int f){
edge[cnt] = (Edge){v, ft[u], f}; ft[u] = cnt++;
}
IL void Dfs(RG int u, RG int fa){
sum[u] = w[u];
uev(e, u){
RG int v = edge[e].to, f = edge[e].f;
if(v == fa) continue;
Dfs(v, u);
sum[u] += sum[v];
dis[u] += dis[v] + sum[v] * f;
}
}
IL void Dfs2(RG int u, RG int fa, RG ll D, RG ll S){
ans = min(ans, D);
uev(e, u){
RG int v = edge[e].to, f = edge[e].f;
if(v == fa) continue;
RG ll ss = S - sum[v], dd = D - sum[v] * f + ss * f;
Dfs2(v, u, dd, S);
}
}
int main(){
mem(ft, -1);
n = Get();
rep(i, 1, n) w[i] = Get();
rep(i, 1, n - 1){
RG int u = Get(), v = Get(), f = Get();
Add(u, v, f); Add(v, u, f);
}
Dfs(1, 0);
Dfs2(1, 0, dis[1], sum[1]);
printf("%lld
", ans);
return 0;
}