跳转至

P1967 [NOIP 2013 提高组] 货车运输

2025-05-27

看着不太会,于是果断打开题解。果然稍微有点难想,需要树上倍增求LCA,我不咋熟,也是后面基本没用过。树剖还稍微写了几题。

tag: LCA 生成树 倍增

hhh,还有好多篇博客没有写,很多题目没有补,也快要期末考试了,下周是最后一个有课的周,端午打算回家玩玩。

题意

给一个 \(n\) 个点 \(m\) 条边的无向图,有 \(q\) 次询问,每次询问 \(x\)\(y\) 的路径经过的最小边权最大是多少。

思路

如果是一个树,那就很好考虑路径问题,但是图就不好搞。但这题,只需要考虑路径上的最小的边,可以把这个图上很多无用的边去掉,只需要保留最大生成树。还有哪些情况可以像这样转换呢?然后就变成树上的问题了,两点之间的路径变成了唯一的,只需要 LCA 来求路径,预处理一下经过的最小的边。

代码

lca 代码还挺好懂的,就是先让两个点深度相同,然后若它们深度相同后仍为不同的点,那么它们的 lca 在深度更小的节点。这里使用 \(e_{u,i}\) 来求 \(u\) 到第 \(2^{i}\) 个祖先的路径上经过的最短的边,跟 lca 一样的。

// #define YUANSHEN
#if defined(YUANSHEN)
#include "C:/codes/cp_code/template/debug.hpp"
#else
#include <bits/stdc++.h>
using namespace std;
#define dbg(...) 42
#endif

#define rep(N) for(int i = 1; i <= N; i++)

template <typename T1, typename T2>
void cmin(T1& x, const T2& y)
{
    x = x < y ? x : y;
}
template <typename T1, typename T2>
void cmax(T1& x, const T2& y)
{
    x = x > y ? x : y;
}
using ll = long long;
using ull = unsigned long long;
using vi = vector<int>;
using vl = vector<ll>;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
template <class T>
using vc = vector<T>;

#define fixset(x) fixed << setprecision(x)
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define ALL(x) (x).begin() + 1, (x).end()
constexpr int INF = 1000000000;
constexpr ll LNF = 1000000000000000000LL;

// Date: 2025-05-27
// Time: 17:01:29
int fa[10005][21], e[10005][21];

void ChatGptDeepSeek()
{
    int n, m;
    cin >> n >> m;
    vector<array<int, 3>> edge(m);
    for(int i = 0; i < m; i++)
        cin >> edge[i][0] >> edge[i][1] >> edge[i][2];
    vi f(n + 1);
    vc<vc<array<int, 2>>> g(n + 1);
    auto find = [&](auto &&self, int x) -> int{
        return f[x] == x ? x : f[x] = self(self, f[x]);
    };
    auto kruskal = [&](){
        ranges::sort(edge, [](array<int, 3> x, array<int, 3> y){
            return x[2] > y[2];
        });
        for(int i = 1; i <= n; i++)
            f[i] = i;
        for(auto [u, v, w] : edge){
            if(find(find, u) != find(find, v)){
                f[f[v]] = f[u];
                g[u].push_back({v, w});
                g[v].push_back({u, w});
            }
        }
    };
    kruskal();
    vi dep(n + 1);
    auto dfs = [&](auto &&self, int u, int pre) -> void{
        dep[u] = dep[pre] + 1;
        for(auto [v, w] : g[u]){
            if(v == pre) continue;
            fa[v][0] = u, e[v][0] = w;
            self(self, v, u);
        }
    };

    for(int i = 1; i <= n; i++){
        if(dep[i] == 0){
            dfs(dfs, i, 0);
            fa[i][0] = i;
            e[i][0] = INF;
        }
    }
    for(int i = 1; i <= 20; i++){
        for(int j = 1; j <= n; j++){
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
            e[j][i] = min(e[j][i - 1], e[fa[j][i - 1]][i - 1]);
        }
    }
    auto lca = [&](int x, int y){
        if(find(find, x) != find(find, y)) return -1;
        if(dep[x] < dep[y]) swap(x, y);
        int res = INF;
        for(int i = 0, z = dep[x] - dep[y]; z; i++, z >>= 1){
            if(z & 1){
                cmin(res, e[x][i]);
                x = fa[x][i];
            }
        }
        if(x == y) return res;
        for(int i = 20; x != y && i >= 0; i--){
            if(fa[x][i] != fa[y][i]){
                cmin(res, min(e[x][i], e[y][i]));
                x = fa[x][i], y = fa[y][i];
            }
        }
        cmin(res, min(e[x][0], e[y][0]));
        return res;
    };
    int q;
    cin >> q;
    while(q--){
        int x, y;
        cin >> x >> y;
        cout << lca(x, y) << '\n';
    }
}

int main()
{
#ifndef YUANSHEN
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
#endif
    int T = 1;
    // cin >> T;
    while (T--)
        ChatGptDeepSeek();
    return 0;
}