[USACO10MAR] Great Cow Gathering G
题目描述
Bessie 正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。
每个奶牛居住在 N N N 个农场中的一个,这些农场由 N − 1 N-1 N−1 条道路连接,并且从任意一个农场都能够到达另外一个农场。道路 i i i 连接农场 A i A_i Ai 和 B i B_i Bi,长度为 L i L_i Li。集会可以在 N N N 个农场中的任意一个举行。另外,每个牛棚中居住着 C i C_i Ci 只奶牛。
在选择集会的地点的时候,Bessie 希望最大化方便的程度(也就是最小化不方便程度)。比如选择第 X X X 个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和(比如,农场 i i i 到达农场 X X X 的距离是 20 20 20,那么总路程就是 C i × 20 C_i\times 20 Ci×20)。帮助 Bessie 找出最方便的地点来举行大集会。
输入格式
第一行一个整数 N N N 。
第二到 N + 1 N+1 N+1 行:第 i + 1 i+1 i+1 行有一个整数 C i C_i Ci。
第 N + 2 N+2 N+2 行到 2 N 2N 2N 行:第 i + N + 1 i+N+1 i+N+1 行为 3 3 3 个整数: A i , B i A_i,B_i Ai,Bi 和 L i L_i Li。
输出格式
一行一个整数,表示最小的不方便值。
样例 #1
样例输入 #1
5
1
1
0
0
2
1 3 1
2 3 2
3 4 3
4 5 3
样例输出 #1
15
提示
1 ≤ N ≤ 1 0 5 1\leq N\leq 10^5 1≤N≤105, 1 ≤ A i ≤ B i ≤ N 1\leq A_i\leq B_i\leq N 1≤Ai≤Bi≤N, 0 ≤ C i , L i ≤ 1 0 3 0 \leq C_i,L_i \leq 10^3 0≤Ci,Li≤103。
依次枚举每一个农场作为集会地点(根节点),求出最小代价,其中的最小值就是问题的答案。
定义状态:d[i]表示以i为根的子树中的牛到节点i所需要的最小代价。
状态转移:设j是i的孩子节点,cnt[j]是子树j中牛的数量,w(i, j)是i->j的边的权值。
① 子树j中的牛如果要到达i,需要先到j,代价为d[j],再从j到i,代价为:cnt[j] *w(i,j);
② 状态转移方程:d[i] = sum(d[j] + cnt[j] * w(i, j)); 问题:单次计算时间复杂度O(N + M),总时间复杂度O(N * (N+M))会超时。
新增状态:f[i]表示以i作为整个树的根(聚会地点)时的最小代价,答案ans = min(f[i]);
性能优化:假设以节点1为根,已经计算出了所有的d[i]: 1. f[1] = d[1] 2. 考虑非1号节点以外的任意节点v,设u是以1为根的树中v的父节点,如下图所示,假设f[u]已求出,考虑如何求解f[v],f[v]可以由两部分组成:
① 以1为根的树中子树v中的节点走过来的,最小代价为d[v]。
② 从u走过来的牛,这些牛要先走到u,再从u走到v,设sum为牛的总数。a) 有sum-cnt[v]头牛会先走到u,代价为:f[u]-d[v]-cnt[v]*w(u,v). b) 接下来这些牛要从u->v,代价为:(sum-cnt[v])*w(u,v)
③ 可得:f[v] = f[u] + (sum - 2*cnt[v]) * w(u, v);
f[1]可以直接求得,接下来就可以按照dfs的顺序依次次计算出所有的f[i]。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100005
#define ll long long
using namespace std;
ll cnt[N],c[N],f[N],g[N],sum,ans=0x3f3f3f3f3f3f3f3f;
struct Edge{
int to ,nxt,v;
}edge[N*2];
int hd[N],tot;
int n;
void add(int u , int v , int d){
edge[++tot].to=v;
edge[tot].v=d;
edge[tot].nxt=hd[u];
hd[u]=tot;
}
void dp(int x,int fa){
cnt[x]=c[x];
for(int i=hd[x];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa) continue;
dp(v,x);
cnt[x]+=cnt[v];
f[x]+=f[v]+cnt[v]*edge[i].v;
}
}
void dfs(int x,int fa){
for(int i = hd[x]; i; i = edge[i].nxt){
int v = edge[i].to;
if(v == fa) continue;
g[v]=g[x]+(sum-2*cnt[v])*edge[i].v;
dfs(v,x);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&c[i]);
sum += c[i];
}
for(int i=1;i<n;i++){
int u,v,d;
scanf("%d%d%d",&u ,&v,&d);
add(u,v,d);
add(v,u,d);
}
dp(1,0);
g[1]=f[1];
dfs(1,0);
for(int i=1;i<=n;i++) ans=min(ans,g[i]);
cout<<ans;
return 0;
}