Description
一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有K个人(分布在K个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。
请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。
Input
第一行两个数,n,K。
接下来n-1行,每行三个数,x,y,z表示x到y之间有一条需要花费z时间的边。
接下来K行,每行一个数,表示K个人的分布。
Output
输出n个数,第i行的数表示:如果在第i个点举行聚会,司机需要的最少时间。
Sample Input
7 2
1 2 4
1 3 1
2 5 1
2 4 2
4 7 3
4 6 2
3
7
Sample Output
11
15
10
13
16
15
10
HINT
【数据规模】
K <= N <= 500000
1 <= x,y <= N, 1 <= z <= 1000000
解题思路:
我们把关键点构成的树的总长叫做虚树,设虚树直径两端为a,b
很容易发现从一个点i出发的最小代价为“(虚树的总路程+i到虚树的距离)*2-max(dis(i,a),dis(i,b))”.
四遍dfs即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=500005;
int n,m,rt;
ll totlen,dis1[N],dis2[N];
int tot,first[N],to[N<<1],nxt[N<<1],w[N<<1],size[N],key[N],in[N];
void add(int x,int y,int z)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
void dfs1(int u,int fa)
{
size[u]=key[u];
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(v==fa)continue;
dis1[v]=dis1[u]+w[e];
if(key[v]&&dis1[v]>dis1[rt])rt=v;
dfs1(v,u);
if(size[v])totlen+=w[e];
size[u]+=size[v];
}
in[u]=(size[u]>0);
}
void dfs2(int u,int fa,ll *dis)
{
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(v==fa)continue;
dis[v]=dis[u]+w[e];
if(key[v]&&dis[v]>dis[rt])rt=v;
dfs2(v,u,dis);
}
}
void dfs3(int u,int fa)
{
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(v==fa)continue;
dis2[v]=(in[v]?0:dis2[u]+w[e]);
dfs3(v,u);
}
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint();
if(!m)
{
for(int i=1;i<=n;i++)puts("0");
return 0;
}
for(int i=1;i<n;i++)
{
int x=getint(),y=getint(),z=getint();
add(x,y,z),add(y,x,z);
}
for(int i=1;i<=m;i++)key[rt=getint()]=1;
dfs1(rt,0);
memset(dis1,0,sizeof(dis1));
dfs2(rt,0,dis1),dfs2(rt,0,dis2);
for(int i=1;i<=n;i++)dis1[i]=max(dis1[i],dis2[i]);
memset(dis2,0,sizeof(dis2));
dfs3(rt,0);
for(int i=1;i<=n;i++)
printf("%lld\n",totlen*2+dis2[i]*2-dis1[i]);
return 0;
}