

思路题。
发现这些农场构成了一棵树。
所以首先以任意一个点为根建树,并把这个点当做目标点。
那么最终的答案就是每条树边乘有几头牛要走,一条边有几头牛要走其实就是这条边下面有几个子孙。
于是我们预处理出以1为根的树的答案,并求出每个节点有几个子孙,然后O(1)转移到他的儿子的答案,这个儿子又可以O(1)转移到他自己的儿子。。。
因此我们用O(n)时间就求出以每个点为目的地的路程和,输出最小即可。
转移方法是:
根变成儿子的时候,只有连接儿子的边要变化,根据预处理出的每个节点的儿子,即可转移。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define M 200000+5
#define LL long long
using namespace std;
int tot=0,h[M],n,c[M],cnt;
LL ans;
struct edge
{
int y,ne;
LL v;
}e[M*2];
struct data
{
int son;
LL l;
}a[M];
void Addedge(int x,int y,LL l)
{
e[++tot].y=y;
e[tot].ne=h[x];
e[tot].v=l;
h[x]=tot;
}
void Prepare(int x,int fa)
{
for (int i=h[x];i;i=e[i].ne)
{
int y=e[i].y;
if (y==fa) continue;
Prepare(y,x);
a[x].son+=a[y].son;
a[x].l=a[x].l+a[y].l+(1LL*e[i].v*a[y].son);
}
a[x].son+=c[x];
}
void dfs(int x,int fa,LL now)
{
for (int i=h[x];i;i=e[i].ne)
{
int y=e[i].y;
if (y==fa) continue;
LL k=now+1LL*e[i].v*(cnt-a[y].son*2);
ans=min(ans,k);
dfs(y,x,k);
}
}
int main()
{
scanf("%d",&n);
cnt=0;
for (int i=1;i<=n;i++)
scanf("%d",&c[i]),cnt+=c[i];
for (int i=1;i<n;i++)
{
int x,y;
LL l;
scanf("%d%d%lld",&x,&y,&l);
Addedge(x,y,l);
Addedge(y,x,l);
}
Prepare(1,0);
ans=a[1].l;
dfs(1,0,a[1].l);
cout<<ans<<endl;
return 0;
}
【BZOJ 1827】 [Usaco2010 Mar]gather 奶牛大集会
原文地址:http://blog.csdn.net/regina8023/article/details/44341749