题目大意
FJ王国有N个村庄组成,村庄之间构成树型结构,村庄之间的公路是双向的。由于发洪水,现在FJ召集了K名奶牛志愿者,每名志愿者被派遣到一个村庄支援救助,不同的志愿者被派遣到不同的村庄。FJ要选择一个村庄作为救灾大本营,一开始所有的志愿者都在该村庄,FJ自己作为司机,要用汽车把这K名志愿者送到各自目的地。FJ出发前会把所有奶牛志愿者都装到他的汽车上,然后按照FJ自己设计的路线,最终把所有奶牛送到目的地。FJ最后不必返回大本营。由于村庄之间的公路的长度都不同,那么FJ应该如何设计行走路线才能使得汽车行驶总长度最短?为了更具体了解情况,FJ提问N个问题,第i个问题是:如果把大本营设置在第i个村庄,那么汽车行驶总长度最短是多少?其中1<=i<=N。你要依次回答这N个问题。
解题思路
考虑第i个询问,即i与K个点最小生成树减去i到K个点中最远点的距离。考虑K个点最小生成树加上两倍i到生成树上的距离减去i到K个点中最远点的距离。生成树可以选K个点中一个点dfs一次求解,有了生成树i到生成树上的距离也非常容易求出。i到K个点中最远点的距离由两部分组成,一个是子树中的部分mx,这个很容易求出,一个是剩余的部分g。v是u的父亲,g[u]=max(g[u],g[v]+dis(u,v))。mx不在u的子树,g[u]=max(g[u],mx[v]+dis(u,v))。mx在u的子树,g[u]=max(g[u],mx’[v]+dis(u,v)),mx’是次大值。到此问题解决。
code
#include
#include
#include
#include
#define LF double
#define LL long long
#define min(a,b) ((ab)?a:b)
#define fo(i,j,k) for(LL i=j;i<=k;i++)
#define fd(i,j,k) for(LL i=j;i>=k;i--)
using namespace std;
LL const maxn=5*1e5;
LL n,K,gra,sum,to[maxn*2+10],len[maxn*2+10],next[maxn*2+10],
begin[maxn+10],cnt[maxn+10],f[maxn+10],g[maxn+10],dep[maxn+10],
mx[maxn+10],mx2[maxn+10],mxbel[maxn+10];
void insert(LL u,LL v,LL w){
to[++gra]=v;
len[gra]=w;
next[gra]=begin[u];
begin[u]=gra;
}
void dfs(LL now,LL pre){
for(LL i=begin[now];i;i=next[i])if(to[i]!=pre){
dep[to[i]]=dep[now]+len[i];
dfs(to[i],now);
cnt[now]+=cnt[to[i]];
if(cnt[to[i]]){
if(mx[to[i]]+len[i]>mx[now]){
mx2[now]=mx[now];
mx[now]=mx[to[i]]+len[i];
mxbel[now]=to[i];
}else mx2[now]=max(mx2[now],mx[to[i]]+len[i]);
sum+=len[i]*2;
}
}
}
void dfss(LL now,LL pre,LL tmp){
f[now]=dep[now]-dep[tmp];
for(LL i=begin[now];i;i=next[i])if(to[i]!=pre){
if(mxbel[now]!=to[i])g[to[i]]=max(g[now],mx[now])+len[i];
else g[to[i]]=max(g[now],mx2[now])+len[i];
dfss(to[i],now,(cnt[to[i]])?to[i]:tmp);
}
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%lld%lld",&n,&K);LL u,v,w,st;
fo(i,2,n){
scanf("%lld%lld%lld",&u,&v,&w);
insert(u,v,w);insert(v,u,w);
}
fo(i,1,K)scanf("%lld",&u),cnt[u]++,st=u;
dep[st]=1;dfs(st,0);
dfss(st,0,st);
fo(i,1,n)printf("%lld\n",sum+f[i]*2-max(g[i],mx[i]));
return 0;
}