链接:点击打开链接
题意:一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。有K个人(分布在K个不同的点)要集中到一个点举行聚会。聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。
代码:
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
struct node{
long long to,cost;
};
vector<node> G[500005];
long long k,cp[500005],vis[500005];
long long dp[500005],son[500005],sum[500005][2];
void dfs1(long long s){ //dp[s]表示s到标记点的距离和,sum[s][0]表示s到
long long i,tmp; //标记点的最大距离,sum[s][1]表示次大距离,所以
vis[s]=1; //知道每一个点的答案为dp[s]*2-sum[s][0]
for(i=0;i<G[s].size();i++){
tmp=G[s][i].to;
if(vis[tmp])
continue;
dfs1(tmp);
dp[s]+=dp[tmp];
son[s]+=son[tmp];
if(son[tmp]){ //相当于这个点可以停在某个点,而不是单纯的距离和
dp[s]+=G[s][i].cost;
if(sum[tmp][0]+G[s][i].cost>sum[s][0]){
cp[s]=tmp; //更新最长和次长,并记录最长上的点
sum[s][1]=sum[s][0];
sum[s][0]=sum[tmp][0]+G[s][i].cost;
}
else if(sum[tmp][0]+G[s][i].cost>sum[s][1])
sum[s][1]=sum[tmp][0]+G[s][i].cost;
}
}
}
void dfs2(long long s){
long long i,cnt,tmp;
vis[s]=1;
for(i=0;i<G[s].size();i++){
tmp=G[s][i].to;
if(vis[tmp])
continue;
if(son[tmp]==k) //看标记节点与当前点的位置关系
dp[tmp]=dp[s]-G[s][i].cost;
else if(son[tmp]==0)
dp[tmp]=dp[s]+G[s][i].cost;
else
dp[tmp]=dp[s];
if(cp[s]==tmp)
cnt=sum[s][1]+G[s][i].cost;
else
cnt=sum[s][0]+G[s][i].cost;
if(cnt>sum[tmp][0]){ //看是否向根节点方向走后更长
cp[tmp]=0;
sum[tmp][1]=sum[tmp][0];
sum[tmp][0]=cnt;
}
else if(cnt>sum[tmp][1])
sum[tmp][1]=cnt;
dfs2(tmp);
}
}
int main(){ //其实这类题都是由树的直径进行变形,求最长和次长,
long long n,i,j,x,y,z; //并由根节点推出所有节点
while(scanf("%lld%lld",&n,&k)!=EOF){
for(i=1;i<n;i++){
scanf("%lld%lld%lld",&x,&y,&z);
G[x].push_back((node){y,z});
G[y].push_back((node){x,z});
}
memset(son,0,sizeof(son));
for(i=0;i<k;i++){
scanf("%lld",&x);
son[x]=1;
}
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
memset(vis,0,sizeof(vis));
dfs1(1);
memset(vis,0,sizeof(vis));
dfs2(1);
for(i=1;i<=n;i++)
printf("%lld\n",dp[i]*2-sum[i][0]);
}
return 0;
}