题目链接:点击查看
题意:n个城市节点构成的一棵树,节点i到节点j的电量损耗为 I*I*R*(i到j的路径所含边数),现在要在某个结点上修建一个供电站,使得这个结点到所有其它节点的总损耗量最小。
题解:I*I*R可以提出来,剩下的就求,选择一个点,求其他点到这个点的距离和最小就可以了,树形dp,先求一个节点,记录每个节点子代的个数,逐步向下求即可
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=50010;
ll dp[N],son[N];
int n;
ll I,R;
vector<int> v[N];
void dfs1(int u,int fa)
{
dp[u]=0;
son[u]=1;
for(int i=0;i<v[u].size();i++)
{
int to=v[u][i];
if(to==fa) continue;
dfs1(to,u);
dp[u]+=dp[to]+son[to];
son[u]+=son[to];
}
}
ll minn;
int ans[N],len;
void dfs2(int u,int fa)
{
minn=min(minn,dp[u]);
for(int i=0;i<v[u].size();i++)
{
int to=v[u][i];
if(to==fa) continue;
dp[to]=dp[u]-son[to]+(n-son[to]);// 子代的孩子数就不走这条边了,剩下的节点要走这条边
dfs2(to,u);
}
}
void dfs3(int u,int fa)
{
if(minn==dp[u])
{
ans[++len]=u;
}
for(int i=0;i<v[u].size();i++)
{
int to=v[u][i];
if(to==fa) continue;
dfs3(to,u);
}
}
int main()
{
int T;
int x,y;
scanf("%d",&T);
while(T--)
{
scanf("%d%lld%lld",&n,&I,&R);
for(int i=1;i<=n;i++)v[i].clear();
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
minn=1e18,len=0;
dfs1(1,0); // 先求节点1
dfs2(1,0); // 求子代 和 最小值
dfs3(1,0); // 求 有多少最小值
sort(ans+1,ans+1+len);
printf("%lld\n",I*I*R*minn);
for(int i=1;i<=len;i++)
printf("%d%c",ans[i]," \n"[i==len]);
printf("\n");
}
return 0;
}