一个很 nb 的古老周赛题 R9T6
Problem
给你一棵树,树上有 \(k\) 个点被染色了,问你对于每个 \(i\),以 \(i\) 为起点遍历每一个被染色的点所经过路径长度的最小值。
Solution
对于单个询问可以做到 \(O(N)\)。
定义 \(f_x\) 表示以 \(x\) 为根遍历过其子树内所有被染色的点最后回到 \(x\) 的路径最小值,转移很简单是
\[f_x=\sum\limits_{v\in son_u} f_v+w_{u\to v} \times 2
\]
意义是 \(u\to v \to u\)。
因为单个询问 \(x\) 就是整棵树的根所以答案是 \(f_x\) 减去 \(x\) 到某个被染色的点的最长链的长度。
最长链包括次长链都很好维护,哦还要为下面的换根预处理出 \(siz_x\) 表示 \(x\) 的子树内有多少个被染色的点。
考虑换根,定义 \(g_x\) 表示以 \(x\) 为根遍历过所有被染色的点最后回到 \(x\) 的路径最小值,因为是全局路径,所以还要维护 \(x\) 到不在 \(x\) 的子树内某个被染色的点的最长链长度,转移分三种情况讨论:
- 被染色的点全在 \(x\) 的子树内,则 \(up_x=0,g_x=f_x\)。
- 被染色的点全都不在 \(x\) 的子树内,则 \(up_x=\max(up_{fa},fl_{fa})+w_{fa\to u}\),解释一下为什么 \(up_x\) 这么更新,因为 \(up_x\) 到最远被染色点 \(y\) 可能有两种情况,一种是 \(dep_y<dep_x\),则此情况 \(up_x=up_{fa}+w_{fa\to u}\),另一种是 \(dep_y>dep_x\),则意义是从 \(x\) 一直向上跳跳到一个分叉点又向下走走到一个被染色的点 \(y\),即此时 \(up_x=fl_{fa}+w_{fa\to u}\)。
- 其余情况,\(g_x=g_{fa}\),\(up\) 用最长链和次长链更新即可,这个比第二种好理解。
复杂度是 \(O(N)\) 的。
点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
using LL = long long;
using namespace std;
const int N = 1e5 + 5;
const int M = 105;
const int mod = 1e9 + 7;
#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)
LL n, m, k, u, v, w, ans[N], f[N], g[N], sl[N], fl[N], siz[N], up[N];
vector<PII> e[N];
void dfs1(int x, int fa) {
for(auto [y, w] : e[x]) {
if(y == fa) continue;
dfs1(y, x);
if(!siz[y]) continue;
siz[x] += siz[y], f[x] += f[y] + w * 2;
if(fl[y] + w > fl[x]) sl[x] = fl[x], fl[x] = fl[y] + w;
else if(fl[y] + w > sl[x]) sl[x] = fl[y] + w;
}
}
void dfs2(int x, int fa, int ltw) {
if(siz[x] == k) up[x] = 0, g[x] = f[x];
else if(siz[x] == 0) up[x] = max(up[fa], fl[fa]) + ltw, g[x] = g[fa] + ltw * 2;
else {
g[x] = g[fa];
if(fl[fa] - fl[x] == ltw) up[x] = max(up[fa], sl[fa]) + ltw;
else up[x] = max(up[fa], fl[fa]) + ltw;
}
for(auto [y, w] : e[x]) {
if(y == fa) continue;
dfs2(y, x, w);
}
}
signed main() {
FASTIO;
cin >> n >> k;
rep(i, 1, n - 1) cin >> u >> v >> w, e[u].pb({v, w}), e[v].pb({u, w});
rep(i, 1, k) cin >> u, siz[u] = 1;
dfs1(1, 0), dfs2(1, 0, 0);
// rep(i, 1, n) cout << f[i] << ' ' << fl[i] << ' ' << sl[i] << ' ' << siz[i] << ' ' << '\n';
rep(i, 1, n) cout << g[i] - max(up[i], fl[i]) << '\n';
return 0;
}

浙公网安备 33010602011771号