首页 > 代码库 > [USACO10MAR]伟大的奶牛聚集
[USACO10MAR]伟大的奶牛聚集
[USACO10MAR]伟大的奶牛聚集
Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。
每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场。道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000)。集会可以在N个农场中的任意一个举行。另外,每个牛棚中居住者C_i(0 <= C_i <= 1,000)只奶牛。
在选择集会的地点的时候,Bessie希望最大化方便的程度(也就是最小化不方便程度)。比如选择第X个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场X的距离是20,那么总路程就是C_i*20)。帮助Bessie找出最方便的地点来举行大集会。 ——by洛谷(感谢洛谷少有的良心翻译)
http://daniu.luogu.org/problem/show?pid=2986
《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《
建图,然后把它当做以任意点为根的树,然后很容易想用树DP。我们发现a与其父节点b;a为集合点的路径有两类:
- 直接到a;(我们把到a路径符合此类的点集记为A);
- 先到b;(我们把到a路径符合此类的点集记为B);
于是当我们知道f[b]时,f[a]即为在f[b]的基础上A中点不必走a->b,B中点要再走b->a,而A即是a的子树点集;
得方程:
f[a]=f[fa[a]]-tree[a]*dis(a->b)+(tree[root]-tree[a])*dis(a->b);
(想象所有点先聚集于b,再全走到a,其中a的子树上节点多走了,故减去)
代码如下:
#include<cstdio> using namespace std; int n; int c[100001]; long long f1[100001],f[100001]; struct ss { int next,to,dis; }x[200000]; int first[100001],num; long long all; void build(int f,int t,int d) { x[++num].next=first[f]; x[num].to=t; x[num].dis=d; first[f]=num; } long long dfs(int ,int ); void dp(int ,int ,int ); int main() { scanf("%d",&n); int i,j,k,l; for(i=1;i<=n;i++) scanf("%d",&c[i]),all+=c[i]; for(i=1;i<=n-1;i++) { scanf("%d%d%d",&j,&k,&l); build(j,k,l); build(k,j,l); } f[0]=dfs(1,-1); dp(1,0,0); all=100000000000000000; for(i=1;i<=n;i++) if(f[i]<all) all=f[i]; printf("%lld",all); return 0; } long long dfs(int fa,int last) { int j; long long sum=0; j=first[fa]; f1[fa]=c[fa]; while(j) { if(x[j].to!=last) { sum+=dfs(x[j].to,fa)+x[j].dis*f1[x[j].to]; f1[fa]+=f1[x[j].to]; } j=x[j].next; } return sum; } void dp(int fa,int last,int di) { int j; f[fa]=f[last]-f1[fa]*di+(all-f1[fa])*di; j=first[fa]; while(j) { if(x[j].to!=last) dp(x[j].to,fa,x[j].dis); j=x[j].next; } }
祝AC;
[USACO10MAR]伟大的奶牛聚集