【题目描述】
一颗树 $n$ 个点,$n-1$ 条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有 $K$ 个人(分布在 $K$ 个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这 $K$ 个人分别送回去。
请你回答,对于 $i=1 \cdots n$,如果在第 $i$ 个点举行聚会,司机最少需要多少时间把 $K$ 个人都送回家。
【输入格式】
第一行两个数,$n,K$。
接下来 $n-1$ 行,每行三个数,$x,y,z$ 表示 $x$ 到 $y$ 之间有一条需要花费 $z$ 时间的边。
接下来 $K$ 行,每行一个数,表示 $K$ 个人的分布。
【输出格式】
输出 $n$ 个数,第 $i$ 行的数表示:如果在第 $i$ 个点举行聚会,司机需要的最少时间。
【样例】
样例输入
7 2
1 2 4
1 3 1
2 5 1
2 4 2
4 7 3
4 6 2
3
7
样例输出
11
15
10
13
16
15
10
【数据范围与提示】
$ K \le N \le 500000 $
$ 1 \le x,y \le N, 1 \le z \le 1000000 $
【题解】
看到给出 $k$ 个点,考虑虚树。所以这题并不需要虚树(当然如果你想建没人拦你)。
题意是给定一棵树,求从任意点出发走过所有特殊点的最短路径。
先考虑回到出发点的完整回路。由于树上两点间路径只有一条,而每个特殊点都必须到达,所以所有特殊点到出发的路径都至少走了两遍。而显然每条边最多只会走两遍,因此计算所有特殊点到出发点路径的边权和的两倍即可。
考虑不回到出发点。显然可以贪心,找出最远的特殊点,将答案减掉该路径长路一倍即可。
至于换根,是经典的换根 $dp$,$dfs$ 两遍即可维护。注意第二次 $dfs$ 从上往下更新 $dp$ 值时要记录最长链和次长链,保证不会重复。
【代码】
#include<bits/stdc++.h> inline int read ( void ) { int x=0;char ch;bool f=true; while ( !isdigit(ch=getchar()) ) if ( ch==‘-‘ ) f=false; for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48); return f ? x : -x ; } const int maxn=500000+10; int n,k,h[maxn],cnt; bool flag[maxn]; long long ds[maxn],us[maxn],max[maxn],max1[maxn],max2[maxn],ans[maxn]; struct edge { int v,w,nxt; } e[maxn<<1]; inline void dfs ( int u,int fr ) { for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr ) { dfs(e[i].v,u); if ( flag[e[i].v] or ds[e[i].v] ) { ds[u]+=e[i].w+ds[e[i].v]; if ( max1[e[i].v]+e[i].w>max1[u] ) max2[u]=max1[u],max1[u]=max1[e[i].v]+e[i].w; else if ( max1[e[i].v]+e[i].w>max2[u] ) max2[u]=max1[e[i].v]+e[i].w; } } } inline void DFS ( int u,int fr ) { ans[u]=2*(ds[u]+us[u])-std::max(max1[u],max[u]); for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr ) { if ( flag[e[i].v] or ds[e[i].v] ) ds[u]-=ds[e[i].v]+e[i].w; if ( ds[u] or us[u] ) us[e[i].v]=ds[u]+us[u]+e[i].w; if ( max[u] ) max[e[i].v]=max[u]+e[i].w; if ( max1[u] ) { if ( max1[e[i].v]+e[i].w<max1[u] ) max[e[i].v]=std::max(max[e[i].v],max1[u]+e[i].w); else if ( max2[u] ) max[e[i].v]=std::max(max[e[i].v],max2[u]+e[i].w); } DFS(e[i].v,u); if ( flag[e[i].v] or ds[e[i].v] ) ds[u]+=ds[e[i].v]+e[i].w; } } signed main() { n=read();k=read(); for ( int i=1,u,v,w;i<n;i++ ) u=read(),v=read(),w=read(),e[++cnt].nxt=h[u],e[h[u]=cnt].v=v,e[cnt].w=w,e[++cnt].nxt=h[v],e[h[v]=cnt].v=u,e[cnt].w=w; for ( int i=1;i<=k;i++ ) flag[read()]=true; dfs(1,0);DFS(1,0); for ( int i=1;i<=n;i++ ) printf("%lld\n",ans[i]); }
原文:https://www.cnblogs.com/RenSheYu/p/11318300.html