码迷,mamicode.com
首页 > 其他好文 > 详细

[USACO10MAR]伟大的奶牛聚集

时间:2016-12-13 06:17:26      阅读:183      评论:0      收藏:0      [点我收藏+]

标签:return   div   ++   节点   last   while   集合   翻译   ble   

[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为集合点的路径有两类:

  1. 直接到a;(我们把到a路径符合此类的点集记为A);
  2. 先到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]伟大的奶牛聚集

标签:return   div   ++   节点   last   while   集合   翻译   ble   

原文地址:http://www.cnblogs.com/nietzsche-oier/p/6166142.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!