题意:
给一棵树,每条边的权值为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;
}