题意
给出几个点,每个点上有一些人,两个点之间有一条路径,我们要找到一个点使得所有人走到这个点的路径总和最短。
思路
枚举。把这个图看成一个树,我们把每个点和它子节点的人数总和求出来,还有每个点的子节点走到这个点上的路径总和求出来。然后用dfs枚举每一个点作为根节点时的路径总和的最小值并记录下节点就好了。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1000001
using namespace std;
struct node{
int next,to,w;
}e[N];
int head[N],tot,n,x,y,w,v[N];
long long c[N],f[N],peo[N],ans,num[N];
void add(int x,int y,int w) {e[++tot].to=y;e[tot].w=w;e[tot].next=head[x];head[x]=tot;}
void dp1(int x,int dep)
{
v[x]=true;
peo[x]=num[x];//peo[i]是第i个点的子节点和自己的人数总和
f[x]=num[x]*dep;//f[i]表示所有点上的人走到点f的路径总和
for (int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if (!v[y])
{
dp1(y,dep+e[i].w);
f[x]+=f[y];
peo[x]+=peo[y];
}
}
}
void dp2(int x,int last,int from)//from上一条边
{
v[x]=1;
if (x!=1) c[x]=c[last]+(peo[1]-peo[x])*e[from].w-peo[x]*e[from].w;//我们如果设x为集中点,那它的子节点和自己少走了peo[x]*e[from].w,其他的点多走了(peo[1]-peo[x])*e[from].w。
if (c[x]<c[ans]) ans=x;//更新答案
for (int i=head[x];i;i=e[i].next)
{
y=e[i].to;
if (!v[y]) dp2(y,x,i);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%lld",&num[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);
add(y,x,w);
}
c[0]=1e18;//初始化答案为很大的值
dp1(1,0);//求人数和路径
memset(v,0,sizeof(v));
c[1]=f[1];
dp2(1,0,0);//求最后答案
printf("%lld\n%lld",ans,c[ans]);
}