传送门:BZOJ3743Kamp
题意
一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有K个人(分布在K个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。
请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。
输入
第一行两个数,n,K。
接下来n-1行,每行三个数,x,y,z表示x到y之间有一条需要花费z时间的边。
接下来K行,每行一个数,表示K个人的分布。
输出
输出n个数,第i行的数表示:如果在第i个点举行聚会,司机需要的最少时间。
数据规模
K <= N <= 500000
1 <= x,y <= N, 1 <= z <= 1000000
题解
我们任意以某个人的位置结点设为树根dfs构造一颗树。
将所有人的位置结点之间连接,构造出实树(就这样叫),在实树中的结点的答案就是两倍的总链的长度减去以它为根在实树范围内的最长链的长度。
同样对于不在实树范围内的结点,我们可以肯定它是某一个实树内结点为根的子树中的一个结点。所以对于实数范围外的点,答案就是它到最近实树的位置结点的距离加上这个位置结点的答案。
剩下的就是代码实现的问题了。
第一遍dfs求出这颗实树,F[x]指的是在实树内的以结点x为根的子树的边权之和的两倍(由上述结论,我们预先处理成两倍),就是向下走完这颗实树。
第二遍dfs求出dis[x]在整个树范围内的结点x向上走(对于实树内的x,用它的父节点y的dis加上它们之间的边权和(F[y]-F[x]),对于实树外的,则加上x与y之间的边权),就是向上走完这颗实树。
第三遍dfs求出ch[x][0],ch[x][1]在实树范围内的x向下走完(实树外的结点是不可能往下走的)实树的边权之和,ch[x][0]存的是最大值,ch[x][1]存的是次大值(当x在实树中,我们要比较它往上还是往下走答案更小,但有可能它在父节点往下走的最大路径上,这样就无法求出父节点不经过x结点的最大路径来更新答案了,所以我们要存一个次大值,而且要用每个子结点来更新这个次大值,以保证结点x不同时在最大值和次大值的链上(好像不是很有道理))。
第四遍dfs就算整棵树范围内的结点往上走完实树的边权之和,d[x]。
这样处理完之后,我们就可以得到想要的值啦。
对于实树内:
ans[x]=F[x]+dis[x]−max(d[x],ch[x][0]) a