看上去像是一道换根dp什么的
大概有点换根的意思
随便从一个目的地开始搜索,先搜出来 has[x] 表示以 x 为根的子树中有没有目的地
利用 has[] 数组继续搜,搜出 “仅包含目的地和必经点组成的树” 的大小
我们发现“最后走最长的路不回来”这样的方案是最优的,答案就是 (整棵“仅包含目的地和必经点组成的树”的大小 * 2 - 到这个节点最远的目的地的距离)
所以我们还要求出 maxl[x] 代表从 x 出发的最长链长
发现一遍搜索并不能计算出来所有点的 maxl 值
并且发现对于上边这张图的情况在更新 2 号节点时只记录 maxl 是没法更新出正确的 maxl 的
所以我们还需要记录 secl[x] 表示从 x 出发的次长链长
并在转移时限制一下使这两条链没有重边就可以了,只要在更新的时候每个儿子仅更新一次就好了
在第二次对每个节点更新正确的最长链的时候要这样:
ll path = edge[i].val + ((edge[i].val + maxl[y] == maxl[x]) ? (secl[x]) : (maxl[x]));
if(path >= maxl[y]) {
secl[y] = maxl[y];
maxl[y] = path;
} else if(path > secl[y]) secl[y] = path;
来确保父亲节点的最长链与当前节点的最长链没有重边,否则用父亲的次长链来更新
需要注意的是我们求的链一定要是以某一个目的地为结尾的,也需要在搜索时加以限制,将非目的地节点的 maxl 置为 -inf 即可,这样就不会用一个非目的地节点更新其他节点的最长链了
upd:(不过好像在 “仅包含目的地和必经点组成的树”中的直径也有点最长链的意思,毕竟它一定以叶节点为一端且这棵树中叶节点一定是目的地,解释不清是因为这题的一些细节记不起来了
代码:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<map>
using namespace std;
typedef long long ll;
const int MAXN = 500001;
struct EDGE{
int nxt, to;
ll val;
EDGE(int NXT = 0, int TO = 0, ll VAL = 0ll) {nxt = NXT; to = TO; val = VAL;}
}edge[MAXN << 1];
int n, k, totedge, bgn;
int head[MAXN];
ll maxl[MAXN], secl[MAXN], trlen[MAXN];
bool dest[MAXN], has[MAXN];
inline int rd() {
register int x = 0;
register char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) {
x = x * 10 + (c ^ 48);
c = getchar();
}
return x;
}
inline int rdll() {
register ll x = 0ll;
register char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) {
x = x * 10 + (c ^ 48);
c = getchar();
}
return x;
}
inline void add(int x, int y, ll v) {
edge[++totedge] = EDGE(head[x], y, v);
head[x] = totedge;
return;
}
void predfs(int x, int fa) {
bool curres = false;
curres |= dest[x];
for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) {
int y = edge[i].to;
predfs(y, x);
curres |= has[y];
}
has[x] |= curres;
return;
}
void lendfs(int x, int fa) {
for(int i = head[x]; i; i = edge[i].nxt) if(has[edge[i].to] && edge[i].to != fa) {
int y = edge[i].to;
lendfs(y, x);
trlen[x] += trlen[y] + edge[i].val;
}
return;
}
void chndfs(int x, int fa) {
if(dest[x]) maxl[x] = 0ll, secl[x] = -0x3f3f3f3f3f3fll;
else maxl[x] = secl[x] = -0x3f3f3f3f3f3fll;
for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) {
int y = edge[i].to;
chndfs(y, x);
ll path = edge[i].val + maxl[y];
if(path >= maxl[x]) {
secl[x] = maxl[x];
maxl[x] = path;
} else if(path > secl[x]) secl[x] = path;
}
return;
}
void efs(int x, int fa) {
for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) {
int y = edge[i].to;
ll path = edge[i].val + ((edge[i].val + maxl[y] == maxl[x]) ? (secl[x]) : (maxl[x]));
if(path >= maxl[y]) {
secl[y] = maxl[y];
maxl[y] = path;
} else if(path > secl[y]) secl[y] = path;
if(has[y]) trlen[y] = trlen[bgn];
else trlen[y] = edge[i].val + trlen[x];
efs(y, x);
}
return;
}
int main() {
n = rd(); k = rd();
register int xx, yy;
register ll vv;
for(int i = 1; i < n; ++i) {
xx = rd(); yy = rd(); vv = rdll();
add(xx, yy, vv); add(yy, xx, vv);
}
for(int i = 1; i <= k; ++i) {
bgn = rd();
dest[bgn] = true;
}
predfs(bgn, 0);
lendfs(bgn, 0);
chndfs(bgn, 0);
efs(bgn, 0);
for(int i = 1; i <= n; ++i) {
printf("%lld\n", (trlen[i] << 1) - maxl[i]);
}
return 0;
}