T5 旅行 (Day 2) 题解

T5 旅行 (Day 2) 题解

链接:click here

这道题有最大值加上最小值比较麻烦,于是考虑枚举最大值,然后逐个加边,查询最小瓶颈路,于是我们可以先把最小瓶颈树给跑出来,同时算出 $\text{dfs} $ 序,加入一条边时,如果它是树边,直接用并查集连接,如果没有和1相连暂时不更新,否则暴力遍历所有的节点,强行更新答案与最小瓶颈路。如果不是树边,就会将 \(u\) 到 \(\operatorname{LCA}(u,v)\) 和 \(v\) 到 \(\operatorname{LCA}(u,v)\) 上所有的点缩为一点,因为这形成了一个边双,这个节点的点权变为所有被缩点的点权和边权,可以再用一个并查集维护;如果非树边两个端点在同一结点(可以是缩完的点)中,那么直接更新点权即可,答案与最小瓶颈路使用两棵线段树维护即可。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 5;
const int K = 20;
struct DSU {
    vector<int> fa;
    int n;
    DSU (int n) : n(n) {
        fa.assign(n + 1, 0);
        iota(fa.begin(), fa.end(), 0);
    }
    int find(int x) {
        if (x == fa[x]) return x;
        return fa[x] = find(fa[x]);
    }
    void unite(int u, int v) {
        u = find(u), v = find(v);
        if (u == v) return;
        fa[v] = u;
    }
};
#define ls(i) (tree[i].ch[0])
#define rs(i) (tree[i].ch[1])
struct node {
    int mn = 1e12, tag = 1e12;
    int ch[2];
    void apply(int v) {
        mn = min(mn, v);
        tag = min(tag, v);
    }
} tree[N * 40];
int ncnt = 0;
int root1, root2;
void pushdown(int i) {
    if (tree[i].tag != 1e12) {
        if (!ls(i)) ls(i) = ++ncnt;
        if (!rs(i)) rs(i) = ++ncnt;
        tree[ls(i)].apply(tree[i].tag);
        tree[rs(i)].apply(tree[i].tag);
        tree[i].tag = 1e12;
    }
}
void update(int &i, int tl, int tr, int l, int r, int v) {
    if (!i) i = ++ncnt;
    if (l <= tl && tr <= r) {
        tree[i].apply(v);
        return;
    }
    int tm = tl + tr >> 1;
    pushdown(i);
    if (l <= tm)
        update(ls(i), tl, tm, l, r, v);
    if (r > tm)
        update(rs(i), tm + 1, tr, l, r, v);
    tree[i].mn = min(tree[ls(i)].mn, tree[rs(i)].mn);
}
void change(int &i, int tl, int tr, int pos, int v) {
    if (!i) i = ++ncnt;
    if (tl == tr) {
        tree[i].mn = v;
        return;
    }
    int tm = tl + tr >> 1;
    pushdown(i);
    if (pos <= tm)
        change(ls(i), tl, tm, pos, v);
    else
        change(rs(i), tm + 1, tr, pos, v);
    tree[i].mn = min(tree[ls(i)].mn, tree[rs(i)].mn);
}
int query(int i, int tl, int tr, int pos) {
    if (!i) return 1e12;
    if (tl == tr) {
        return tree[i].mn;
    }
    int tm = tl + tr >> 1;
    pushdown(i);
    if (pos <= tm)
        return query(ls(i), tl, tm, pos);
    else
        return query(rs(i), tm + 1, tr, pos);
}

vector<pair<int, int>> g[N];
struct Edge {
    int u, v, w;
    friend bool operator < (Edge x, Edge y) {
        return x.w < y.w;
    }
};
vector<Edge> edge, ne;
int n, m;
void kruskal() {
    sort(edge.begin(), edge.end());
    DSU dsu(n);
    for (auto [u, v, w] : edge) {
        if (dsu.find(u) != dsu.find(v)) {
            dsu.unite(u, v);
            g[u].push_back({v, w});
            g[v].push_back({u, w});
            ne.push_back({u, v, w});
        }
    }
}
int dfn[N], dcnt = 0, siz[N];
int dep[N];
int st[N][K], wf[N];
void dfs1(int u, int fa) {
    dfn[u] = ++dcnt;
    siz[u] = 1;
    st[u][0] = fa;
    dep[u] = dep[fa] + 1;
    for (auto [v, w] : g[u]) {
        if (v == fa) continue;
        wf[v] = w;
        dfs1(v, u);
        siz[u] += siz[v];
    }
}
void init_st() {
    for (int i = 1; i < K; i++) {
        for (int j = 1; j <= n; j++) 
            st[j][i] = st[st[j][i - 1]][i - 1];
    }
}
int jump(int x, int k) {
    for (int i = K - 1; i >= 0; i--) {
        if (k >> i & 1) {
            x = st[x][i];
        }
    }
    return x;
}
int LCA(int u, int v) {
    if (dep[u] > dep[v]) u = jump(u, dep[u] - dep[v]);
    else v = jump(v, dep[v] - dep[u]);
    if (u == v) return u;
    for (int i = K - 1; i >= 0; i--) {
        if (st[u][i] != st[v][i]) {
            u = st[u][i];
            v = st[v][i];
        }
    }
    return st[u][0];
}
int val[N];
int bel[N];
int ans[N];
void dfs(int u, int fa, int c, int mn, DSU &dsu1, int W) {
    mn = min(mn, val[u]);
    mn = min(mn, wf[u]);
    change(root1, 1, n, dfn[u], mn + W);
    change(root2, 1, n, dfn[u], mn);
    for (auto [v, w] : g[u]) {
        if (v == fa) continue;
        if (dsu1.find(v) == c) {
            dfs(v, u, c, mn, dsu1, W);
        }
    }
}
signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        val[i] = 1e12;
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        edge.push_back({u, v, w});
    }
    kruskal();
    dfs1(1, 0);
    init_st();
    DSU dsu1(n), dsu2(n);
    for (auto [u, v, w] : edge) {
        if (dsu1.find(u) == dsu1.find(v)) {
            if (dsu2.find(u) == dsu2.find(v)) {
                int lca = dsu2.find(u);
                val[dsu2.find(u)] = min(val[dsu2.find(u)], w);
                update(root1, 1, n, dfn[lca], dfn[lca] + siz[lca] - 1, val[lca] + w);
                update(root2, 1, n, dfn[lca], dfn[lca] + siz[lca] - 1, val[lca]);
                continue;
            }
            int lca = dsu2.find(LCA(u, v));
            for (int x = dsu2.find(u); x != lca; x = dsu2.find(st[x][0])) {
                dsu2.unite(lca, x);
                val[lca] = min(val[lca], val[x]);
                val[lca] = min(val[lca], wf[x]);
            }
            for (int x = dsu2.find(v); x != lca; x = dsu2.find(st[x][0])) {
                dsu2.unite(lca, x);
                val[lca] = min(val[lca], val[x]);
                val[lca] = min(val[lca], wf[x]);
            }
            update(root1, 1, n, dfn[lca], dfn[lca] + siz[lca] - 1, val[lca] + w);
            update(root2, 1, n, dfn[lca], dfn[lca] + siz[lca] - 1, val[lca]);
        }
        else {
            if (dsu1.find(u) == 1) {
                int mn = query(root2, 1, n, dfn[u]);
                dfs(dsu1.find(v), 0, dsu1.find(v), mn, dsu1, w);
                dsu1.unite(u, v);
            }
            else if (dsu1.find(v) == 1) {
                swap(u, v);
                int mn = query(root2, 1, n, dfn[u]);
                dfs(dsu1.find(v), 0, dsu1.find(v), mn, dsu1, w);
                dsu1.unite(u, v);
            }
            else {
                if (dfn[u] < dfn[v]) dsu1.unite(u, v);
                else dsu1.unite(v, u);
            }
        }
    }
    for (int i = 2; i <= n; i++)
        cout << query(root1, 1, n, dfn[i]) << '\n';
    return 0;
}
posted @ 2026-09-08 10:12  MikotoHohenz  阅读(7)  评论(0)    收藏  举报