【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=1827
【题目大意】
给出一棵有点权和边权的树,
请确定一个点,使得每个点到这个点的距离乘上该点乘积的总和最小。
【题解】
定1为根,我们先计算当这个点为1的时候的值,同时记录每个子树的size
之后我们再做一遍dfs,计算出每个点作为中心时候的答案
我们发现当一个点从父节点往子节点移动的时候
对于答案的变化是ans+=(size[1]-2*size[x])*len(fx->x)
所以我们dfs计算,保留最小值即可。
【代码】
#include <cstdio> #include <algorithm> #include <vector> #include <utility> using namespace std; const int N=100010; typedef pair<int,int> P; typedef long long LL; vector<P> v[N]; int d[N],size[N],n; LL ans,ans1; void dfs(int x,int fx){ for(int i=0;i<v[x].size();i++){ int y=v[x][i].first,z=v[x][i].second; if(y!=fx){ d[y]=d[x]+z; ans+=(LL)size[y]*d[y]; dfs(y,x); size[x]+=size[y]; } } } void dfs2(int x,int fx){ for(int i=0;i<v[x].size();i++){ int y=v[x][i].first,z=v[x][i].second; if(y!=fx){ ans1+=(LL)(size[1]-2*size[y])*z; if(ans1<ans)ans=ans1; dfs2(y,x); ans1-=(LL)(size[1]-2*size[y])*z; } } } int main(){ while(~scanf("%d",&n)){ ans=ans1=0; for(int i=1;i<=n;i++)scanf("%d",&size[i]); for(int i=1;i<n;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); v[a].push_back(P(b,c)); v[b].push_back(P(a,c)); }dfs(1,-1);ans1=ans; dfs2(1,-1); printf("%lld\n",ans); }return 0; }
时间: 2024-10-08 22:41:09