P2986 [USACO10MAR]Great Cow Gathering G(树形DP)
题意:
给出一棵树,
每个点有点权,每条边有边权。
定义答案为所有点自身的点权乘上它到根节点的距离之和。
询问怎么选择根节点使得答案最小。
题解:
计算出每个点子树内的所有点权之和。
计算出每个点子树外的所有点权之和。
换根的时候,答案加上子树外的所有点权之和乘上这条边的权值,减去子树内的所有点权之和乘上这条边的权值,可以做到\(O(1)\)转移。
总体时间复杂度\(O(n)\)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int n;
long long c[maxn];
long long sz[maxn];
vector<pair<int,int> > g[maxn];
long long ans=1e18;
long long sum=0;
void dfs (int x,int pre,long long dep) {
sz[x]=c[x];
sum+=c[x]*dep;
for (pair<int,int> y:g[x]) {
if (y.first==pre) continue;
dfs(y.first,x,dep+y.second);
sz[x]+=sz[y.first];
}
}
void dfs1 (int x,int pre,long long sum) {
ans=min(ans,sum);
for (pair<int,int> y:g[x]) {
if (y.first==pre) continue;
dfs1(y.first,x,sum-sz[y.first]*y.second+(sz[1]-sz[y.first])*y.second);
}
}
int main () {
cin>>n;
for (int i=1;i<=n;i++) cin>>c[i];
for (int i=1;i<n;i++) {
int a,b,l;
cin>>a>>b>>l;
g[a].push_back(make_pair(b,l));
g[b].push_back(make_pair(a,l));
}
dfs(1,0,0);
dfs1(1,0,sum);
printf("%lld\n",ans);
}