看上去像是一道换根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 ", (trlen[i] << 1) - maxl[i]); } return 0; }