题目链接:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28138
题目大意:
有n个小区,有n-1条边将这n个小区连接起来。( 3<=n<=50000)
现在要选择一个小区建设一个电站,小区到电站的电缆是有损耗的。
公式为:I*I*R*Di。
Di表示所有小区到该电站的距离之和,求取最小的损耗。
解题思路:
不能进行暴力枚举,时间复杂度是n^2,会超时。
可以使用树形DP。
建树的方法不一定,看大家自己的喜好。
我定义了3个数组:
Count[i]:i节点的子孙节点的总个数。
dp[i]: i节点的子孙节点到该节点的距离之和。
dis[i]: 非i节点的子孙节点到i节点的距离之和。
那么最后每个点的距离之和就是: dp[i]+dis[i]。
Count[i]数组的求法:
Count[i]+=(Count[j]+1);
j是i的孩子节点,通过从根节点开始的dfs,从后往前递推,可以很快求出该数组。
dp[i]数组的求法:
dp[i]+=(dp[j]+Count[j]+1);
j是i的孩子节点,通过从根节点开始的dfs,从后往前推,可以很快求出该数组。
dis[i]数组的求法:
dis[j]=dis[i]+(dp[i]-dp[j]-1-Count[j])+(N-1-Count[j]);
j是i的孩子节点,别的子树上的节点想要达到j必须先到达j的父亲节点i。
源代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const double eps=1e-8;
int N,I,R,root;
struct node
{
int father;
vector<int> child;
}c[50005];
LL dp[50005]; //以i为根节点的子树当中到i节点的距离之和
int Count[50005]; //i节点的所有子孙节点的个数
void dfs1(int now) //求dp数组
{
int i,j,size,child;
size=c[now].child.size();
if(size==0) return;
for(i=0;i<size;i++)
{
child=c[now].child[i];
dfs1(child);
dp[now]+=(dp[child]+Count[child]+1);
}
return;
}
void dfs2(int now) //求取Count数组
{
int i,j,k,size,child;
size=c[now].child.size();
if(size==0) return;
for(i=0;i<size;i++)
{
child=c[now].child[i];
dfs2(child);
Count[now]+=(Count[child]+1);
}
return;
}
int cnt; //最后可选点的个数
int ans[50005]; //用来记录最后需要输出的数组
LL Min; //用来记录最小值
LL dis[50005]; //别的子树到i点的距离之和。
void dfs(int now)
{
int i,j,k,size,t,child;
size=c[now].child.size();
if(size==0) return;
for(i=0;i<size;i++)
{
child=c[now].child[i];
//要到child,先到child的父亲节点
dis[child]=dis[now]+ //非弄now子树到达now节点
(dp[now]-dp[child]-Count[child]-1)+ //除了child的子孙
(N-1-Count[child]); //需要到达child的节点的个数
dfs(child);
}
return;
}
int main()
{
freopen("in.txt","r",stdin);
int cs,a,b,father,child,i,j;
scanf("%d",&cs);
while(cs--)
{
scanf("%d%d%d",&N,&I,&R);
memset(c,0,sizeof(c));
for(i=1;i<N;i++)
{
scanf("%d%d",&a,&b);
father=a; child=b;
if(c[b].father)
{
father=b;
child=a;
}
c[child].father=father;
c[father].child.push_back(child);
}
root=1;
while(c[root].father)
root=c[root].father;
//求取Count数组
memset(Count,0,sizeof(Count));
dfs2(root);
//求取dp数组,本子树的距离之和
memset(dp,0,sizeof(dp));
dfs1(root);
//求取dis数组,其他子树的距离之和
memset(dis,0,sizeof(dis));
dfs(root);
//输出最后的结果
Min=INF;
cnt=0;
for(i=1;i<=N;i++)
{
if(dp[i]+dis[i]<Min)
{
cnt=0;
ans[cnt++]=i;
Min=dp[i]+dis[i];
}
else if(dp[i]+dis[i]==Min)
{
ans[cnt++]=i;
}
}
printf("%I64d\n",I*I*R*Min);
printf("%d",ans[0]);
for(i=1;i<cnt;i++)
{
printf(" %d",ans[i]);
}
printf("\n");
if(cs)
printf("\n");
}
return 0;
}