P2986 [USACO10MAR] Great Cow Gathering G

原题链接

题解

很简单想到暴力,但是 \(O(n^2)\) 显然不行
所以要减少计算量,如何利用已经计算过的值而不是重新算一遍呢?
这道题最好看成有中心点的网状图,而不是树状图

随便取一个点 \(A\) 作为根节点,很容易计算其答案,如何计算以其他点为根节点的答案呢?
对于以根节点的邻边节点 \(B\) 为根节点的图,其答案相当于原本 \(A\) 点的答案加上 \(A\) 点的所有奶牛迁移到 \(B\) 点的消耗,又因为以 \(B\) 节点为根节点的子树(相对于 \(A\) 来说)重复走了两段
所以减去这个重复的两端就是 \(B\) 点的答案
如果这个过程以 有中心点的网状图 来看很容易理解

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct edge
{
    ll to,val;
};
vector<edge> G[100005];
ll c[100005]={0};
ll sum1[100005]={0},ans1[100005]={0};
ll ans[100005]={0};
ll total=0;
void ss(ll now,ll fa)
{
    sum1[now]=c[now];
    for(ll i=0;i<G[now].size();i++)
    {
        ll next=G[now][i].to,len=G[now][i].val;
        if(next==fa)continue;

        ss(next,now);
        ans1[now]+=sum1[next]*len+ans1[next];
        sum1[now]+=sum1[next];
    }
}

void turn(ll now,ll fa)
{
    for(ll i=0;i<G[now].size();i++)
    {
        ll next=G[now][i].to,len=G[now][i].val;
        if(next==fa)continue;

        ans[next]=(total-2*sum1[next])*len+ans[now];
        turn(next,now);
    }
}

int main()
{
    ll n;
    cin>>n;
    for(ll i=1;i<=n;i++)
    {
        cin>>c[i];
        total+=c[i];
    }

    for(ll i=1;i<=n-1;i++)
    {
        ll x,y,l;
        cin>>x>>y>>l;
        G[x].push_back({y,l});
        G[y].push_back({x,l});
    }

    ss(1,0);
    ans[1]=ans1[1];
    turn(1,0);

    ll sum=2e18;
    for(ll i=1;i<=n;i++)
    {
        //cout<<ans[i]<<endl;
        sum=min(sum,ans[i]);
    }
    cout<<sum<<endl;
    return 0;
}

posted @ 2024-02-07 23:16  纯粹的  阅读(61)  评论(0)    收藏  举报