题解
很简单想到暴力,但是 \(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;
}
标签:Gathering,ll,节点,100005,为根,答案,now,P2986,USACO10MAR
From: https://www.cnblogs.com/pure4knowledge/p/18011468