JLUCPC
题意:N座城市由N-1条边连接,就是一棵树;一直每条边及其距离;每座城市中都有若干球队;现要推选出开一个盟主大会,要让所有球队到该城市消耗体力最少;若i城的球队到j城,消耗体力为team[i]*dis[i][j];
也就是找到这么一个城市k,使得最小;
先求出每个节点(包括子节点)共有多少队伍team[i], 其子节点到父节点的花费之和cost[i];
设dp[i]表示城市选在i处的所有球队消耗;
若城市选在根节点,那么所有球队的消耗为dp[root]=cost[root];
对于除根节点外的任意节点i,若城市选在i,所有球队的消耗为其子节点到i的消耗和cost[i]+剩下的所有球队到i的父节点再到i的消耗;
前一步就是cost[i];后一步:在分解为两步;
剩下的球队到i的父节点的消耗+剩下的球队从i的父节点到i的消耗;
第一步就是dp[i的父节点]-cost[i]-team[v]*dis[i][i的父节点];
第二步:(sum-team[i])*dis[i][i的父节点];
所以dp[i]=cost[i]+dp[fa]-cost[i]-team[i]*dis[i][fa]+(sum-team[i])*dis[i][fa] (fa 是i的父节点);
化简后dp[i]=dp[fa]+sum*dis[i][fa]-team[i]*dis[i][fa]*2;
#include <bits/stdc++.h>
#define INF 1LL << 62
using namespace std;
const int maxn=100100;
typedef long long ll;
ll team[maxn], cost[maxn], sum, dp[maxn];
int head[maxn], cnt;
struct node{
int v, nxt;
ll w;
node(){}
node(int v0, ll w0, int n){
v=v0, w=w0, nxt=n;
}
}edge[maxn<<1];
void add(int u, int v, ll w){
edge[cnt]=node(v, w, head[u]);
head[u]=cnt++;
}
void dfs_team(int u, int fa){
cost[u]=0;
for(int i=head[u]; i!=-1; i=edge[i].nxt){
int v=edge[i].v, w=edge[i].w;
if(v==fa) continue;
dfs_team(v, u);
team[u]+=team[v];
cost[u]+=team[v]*w+cost[v];
}
}
void dfs(int u, int fa){
for(int i=head[u]; i!=-1; i=edge[i].nxt){
int v=edge[i].v, w=edge[i].w;
if(v==fa) continue;
dp[v]=dp[u]-team[v]*w+(sum-team[v])*w;
dfs(v, u);
}
}
int main(){
int N;
while(~scanf("%d", &N)){
sum=0;
for(int i=1; i<=N; i++){
scanf("%lld", &team[i]);
sum+=team[i];
}
memset(head, -1, sizeof(head));
cnt=0;
for(int i=1; i<N; i++){
int a, b;
ll l;
scanf("%d%d%lld", &a, &b, &l);
add(a, b, l);
add(b, a, l);
}
dfs_team(1, 1);
dp[1]=cost[1];
dfs(1, 1);
ll ans=INF;
for(int i=1; i<=N; i++){
if(ans>dp[i]) ans=dp[i];
}
printf("%lld\n", ans);
}
return 0;
}