题意
在一棵树上,边有边权,给定 $k$ 个特殊点,对于每个点 $i$ 求,从 $i$ 出发必须经过所有特殊点的最短路径长度(可重复)。
$n,k\leq10^5$ 。
题解
不能直接对 dfn 排序然后线性做因为不对。
考虑树形 DP,答案就是从 $i$ 出发到每个有点的儿子一来一回,最终减去最长链,也可以跑到父亲那里,换根即可。对于最长链,有时需要次长链更新。
const int N = 5e5 + 5;
inline ll Read() {
ll x = 0, f = 1;
char c = getchar();
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') f = -f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
return x * f;
}
namespace Main {
int n, K;
int head[N], tot;
struct Edge {
ll to, w, nxt;
} e[N << 1];
void add_edge(int u, int v, ll w) { e[++tot] = (Edge) {v, w, head[u]}, head[u] = tot; }
bool key[N];
ll siz[N];
ll f[N], g[N], dis[N][2], id[N];
void dfs1 (int u, int fa) {
if (key[u]) siz[u] = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to; if (v == fa) continue;
dfs1(v, u);
if (siz[v]) {
g[u] += g[v] + e[i].w * 2;
if (dis[v][0] + e[i].w >= dis[u][0]) dis[u][1] = dis[u][0], dis[u][0] = dis[v][0] + e[i].w, id[u] = v;
else if (dis[v][0] + e[i].w > dis[u][1]) dis[u][1] = dis[v][0] + e[i].w;
}
siz[u] += siz[v];
}
}
void dfs2 (int u, int fa) {
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to; if (v == fa) continue;
if (!siz[v]) f[v] = f[u] + 2 * e[i].w, dis[v][0] = dis[u][0] + e[i].w;
else if (!(K - siz[v])) f[v] = g[v];
else {
f[v] = f[u];
if (id[u] != v && dis[v][0] < dis[u][0] + e[i].w) dis[v][1] = dis[v][0], dis[v][0] = dis[u][0] + e[i].w, id[v] = u;
else if (dis[v][0] < dis[u][1] + e[i].w) dis[v][1] = dis[v][0], dis[v][0] = dis[u][1] + e[i].w, id[v] = 1;
else if (id[u] != v && dis[v][1] < dis[u][0] + e[i].w) dis[v][1] = dis[u][0] + e[i].w;
else if (dis[v][1] < dis[u][1] + e[i].w) dis[v][1] = dis[u][1] + e[i].w;
}
dfs2(v, u);
}
}
int main () {
n = Read(), K = Read();
for (int i = 1; i < n; i++) {
int u = Read(), v = Read(), w = Read();
add_edge(u, v, w), add_edge(v, u, w);
}
for (int i = 1; i <= K; i++) key[Read()] = 1;
dfs1(1, 0); f[1] = g[1]; dfs2(1, 0);
for (int i = 1; i <= n; i++) printf ("%lld\n", f[i] - dis[i][0]);
return 0;
}
}
int main () {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
Main::main();
return 0;
}