一个很 nb 的古老周赛题 R9T6

link

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;
}
posted @ 2025-04-01 20:42  Iron_Spade  阅读(35)  评论(0)    收藏  举报