题意:
给一棵树,每条边的权值为R*I^2,求到所有其他节点权值和最小的点和相应最小权值和。
分析:
先将图转化成树,然后在树上dfs。
代码:
//poj 4045 //sep9 #include<iostream> #include<vector> using namespace std; const int maxN=50012; vector<int> g[maxN]; int vis[maxN],bro[maxN],son[maxN]; __int64 num[maxN],dis[maxN],dp[maxN]; int n,I,R; void build_tree(int h) { vis[h]=1; for(int i=g[h].size()-1;i>=0;--i){ int x=g[h][i]; if(vis[x]==0){ vis[x]=1; bro[x]=son[h]; son[h]=x; build_tree(x); } } } void dfs(int h,__int64& num_res,__int64& dis_res) { num_res=1,dis_res=0; int s=son[h]; while(s){ __int64 x,y; dfs(s,x,y); num_res+=x; dis_res+=(x+y); s=bro[s]; } num[h]=num_res; dis[h]=dis_res; } void ex_dfs(int h,__int64 pre_sum) { dp[h]=dis[h]+pre_sum; int s=son[h]; while(s){ __int64 a=n-num[s]; __int64 b=dis[h]-(dis[s]+num[s])+pre_sum; ex_dfs(s,a+b); s=bro[s]; } } int main() { int cases; scanf("%d",&cases); while(cases--){ scanf("%d%d%d",&n,&I,&R); for(int i=1;i<=n;++i) g[i].clear(); for(int i=1;i<n;++i){ int a,b; scanf("%d%d",&a,&b); g[a].push_back(b); g[b].push_back(a); } memset(vis,0,sizeof(vis)); memset(bro,0,sizeof(bro)); memset(son,0,sizeof(son)); build_tree(1); memset(num,0,sizeof(num)); memset(dis,0,sizeof(dis)); __int64 x,y; dfs(1,x,y); ex_dfs(1,0); __int64 ans=dp[1]; for(int i=1;i<=n;++i) ans=min(ans,dp[i]); printf("%I64d\n",ans*R*I*I); for(int i=1;i<=n;++i) if(dp[i]==ans) printf("%d ",i); printf("\n\n"); } return 0; }
时间: 2024-10-14 22:27:20