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);
}
posted @ 2021-04-11 13:46  zlc0405  阅读(100)  评论(0编辑  收藏  举报