P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat…

  一道比较经典的题(蒟蒻的我没有一次切掉)……

  首先这道题要优化的原因就是数据范围,要是O(n2)  “  显然  ” 会超时。。。

  那么我们很不情愿不得已不开心地最终被迫考虑一下优化(说实话好多算法都是为优化而生的啊)。

  首先记录所有点(记得点上有可能有好几头牛)到根节点的路径之和。

  那么每一次考虑,将汇聚的点从父亲改到现在到的这个儿子,那么,以儿子节点作为根的子树上的所有点,就都少走了从儿子到父亲的距离,那么除这些点以外的点,它们都要多走从儿子到父亲的距离。

  那么我们只要一开始随意找一个点作为root,dfs求出距离和,再来一次dfs从下往上不断更新状态就好,每次取最优解就可以了。

  我第一次没A是因为在第二次dfs的时候忘了继续往上dfs(我对自己表示emmm……),第二次是因为没开long long……

  代码如下(请不要学习黑科技):

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 10000005
#define int long long
int num[maxn],dis[maxn],head[maxn];
struct node
{
    int to,nxt,w;
} e[maxn];
int cnt,tot,n,d,ans;
void add(int a,int b,int c)
{
    e[++cnt].to=b;
    e[cnt].nxt=head[a];
    head[a]=cnt;
    e[cnt].w=c;
}
void dfs(int u,int fa)
{
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v==fa) continue;
        dfs(v,u);
        num[u]+=num[v];
        d+=e[i].w*num[v];
    }
}
void dp(int u,int fa)
{
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v==fa) continue;
        dis[v]=dis[u]-num[v]*e[i].w+(tot-num[v])*e[i].w;
        ans=min(ans,dis[v]);
        dp(v,u);
    }
}
main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        int am;
        scanf("%lld",&am);
        num[i]=am;
        tot+=am;
    }
    for(int i=1;i<n;i++)
    {
        int a,b,c; 
        scanf("%lld%lld%lld",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    dfs(1,0);
    dis[1]=d;
    ans=d;
    dp(1,0);
    printf("%lld",ans);
    return 0;
}

 

原文地址:https://www.cnblogs.com/popo-black-cat/p/10206416.html