题目大意:
镇海中学共有n个景点,每个景点均有若干学生正在参观。这n个景点以自然数1至n编号,每两个景点的编号均不同。每两个景点之间有且只有一条路径。选择哪个景点集中的学生,才能使所有学生走过的路径之和最小呢?
解题思路:
先以1为root搜一次,求出num[i]和ans[i],num[i]是从i到1的学生数量总和,ans[i]是从i到1的代价
然后再搜一次,每次加上(第一个景点的学生人数-上个景点的学生人数)×路径长度-这个景点的学生人数×路径长度,然后存于sum[i]中,sum[i]则为以i为集合点的代价
maxn为在最小代价的情况下的集合点
源程序:
#include<cstdio>
#include<cstring>
#define inf 1e18
#define N 1000001
using namespace std;
struct node{
int x,y,w,next;
}a[N];
bool v[N];
int n,len,maxn,xx[N],last[N];
long long num[N],ans[N],sum[N];
void add(int x,int y,int w)
{
a[++len].x=x;a[len].y=y;a[len].w=w;a[len].next=last[x];last[x]=len;
a[++len].x=y;a[len].y=x;a[len].w=w;a[len].next=last[y];last[y]=len;
}
void dfs(int x,int rw)
{
v[x]=1;//已经搜过做标记
num[x]=xx[x];
ans[x]=xx[x]*rw;//计算num和ans
for (int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if (!v[y])
{
dfs(y,rw+a[i].w);//往下搜
ans[x]+=ans[y];//加上搜到的结果
num[x]+=num[y];//加上搜到的结果
}
}
}
int find(int x,int rw,int fa,int from)
{
v[x]=1;
if (x!=1)
sum[x]=sum[fa]+(num[1]-num[x])*a[from].w-num[x]*a[from].w;//计算代价
if (sum[x]<sum[maxn]) maxn=x;//找最小
for (int i=last[x];i;i=a[i].next)
{
int y=a[i].y;
if (!v[y])
find(y,rw+a[i].w,x,i);//往下找
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%lld",&xx[i]);
for (int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
sum[0]=inf;//maxn一开始等于0,没有0这个景点
dfs(1,0);//搜第一遍
sum[1]=ans[1];
memset(v,0,sizeof(v));
find(1,0,0,0);//搜第二遍
printf("%d\n%lld",maxn,sum[maxn]);
}