NC16527 [NOIP2013]货车运输

发布时间 2023-06-23 11:32:51作者: 空白菌

题目链接

题目

题目描述

A 国有 n 座城市,编号从 1 到 n ,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入描述

第一行有两个用一个空格隔开的整数 n,m ,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x, y, z ,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意: x 不等于 y ,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。

输出描述

共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出 -1 。

示例1

输入

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

输出

3
-1
3

备注

对于 30% 的数据, 0 < n < 1,000,0 < m < 10,000,0 < q< 1,000 ;
对于 60% 的数据, 0 < n < 1,000,0 < m < 50,000,0 < q< 1,000 ;
对于 100% 的数据, 0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000 。

题解

知识点:倍增,LCA,并查集,最小生成树。

显然,若两点有多条路径,我们一定选择一条最大生成树上的路径,这样保证走的每条边都是权值最大的,因此我们先求出最大生成树,代替原图。

然后就是查询任意两点路径的最小边,我们可以使用树剖,但是这里问题是静态的,因此用树上倍增会更好写。

注意,每次查询之前先判定两点是否连通,这个可以用并查集实现。

时间复杂度 \(O(m \log m + m \log n + q \log n)\)

空间复杂度 \(O(m+ n \log n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct DSU {
    vector<int> fa;

    DSU(int n = 0) { init(n); }

    void init(int n) {
        fa.assign(n + 1, 0);
        iota(fa.begin(), fa.end(), 0);
    }

    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

    bool same(int x, int y) { return find(x) == find(y); }

    void merge(int x, int y) { fa[find(x)] = find(y); }
};

template<class T>
struct Graph {
    struct edge {
        int v, nxt;
        T w;
    };
    int idx;
    vector<int> h;
    vector<edge> e;

    Graph(int n = 0, int m = 0) { init(n, m); }

    void init(int n, int m) {
        idx = 0;
        h.assign(n + 1, 0);
        e.assign(m + 1, {});
    }

    void add(int u, int v, T w) {
        e[++idx] = { v,h[u],w };
        h[u] = idx;
    }
};

const int N = 10007, M = 50007;
Graph<int> g;

struct node {
    int u, v, w;
}e[M];

bool vis[N];
int f[27][N], mi[27][N], dep[N];
void dfs(int u, int fa, int faw) {
    vis[u] = 1;
    f[0][u] = fa;
    mi[0][u] = faw;
    dep[u] = dep[fa] + 1;
    for (int i = 1;i <= 20;i++) {
        f[i][u] = f[i - 1][f[i - 1][u]];
        mi[i][u] = min(mi[i - 1][u], mi[i - 1][f[i - 1][u]]);
    }
    for (int i = g.h[u];i;i = g.e[i].nxt) {
        int v = g.e[i].v, w = g.e[i].w;
        if (vis[v]) continue;
        dfs(v, u, w);
    }
}

int get_ans(int u, int v) {
    int ans = 1e9;
    if (dep[u] < dep[v]) swap(u, v);
    for (int i = 20;i >= 0;i--) {
        if (dep[f[i][u]] >= dep[v]) {
            ans = min(ans, mi[i][u]);
            u = f[i][u];
        }
        if (u == v) return ans;
    }
    for (int i = 20;i >= 0;i--) {
        if (f[i][u] != f[i][v]) {
            ans = min({ ans, mi[i][u], mi[i][v] });
            u = f[i][u];
            v = f[i][v];
        }
    }
    return min({ ans, mi[0][u], mi[0][v] });
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1;i <= m;i++) {
        int u, v, w;
        cin >> u >> v >> w;
        e[i] = { u,v,w };
    }
    sort(e + 1, e + m + 1, [&](const node &a, const node &b) {return a.w > b.w;});
    DSU dsu(n);
    g.init(n, n << 1);
    for (int i = 1;i <= m;i++) {
        auto [u, v, w] = e[i];
        if (dsu.same(u, v)) continue;
        dsu.merge(u, v);
        g.add(u, v, w);
        g.add(v, u, w);
    }

    for (int i = 1;i <= n;i++) if (!vis[i]) dfs(i, 0, 0);

    int q;
    cin >> q;
    while (q--) {
        int u, v;
        cin >> u >> v;
        if (!dsu.same(u, v)) cout << -1 << '\n';
        else cout << get_ans(u, v) << '\n';
    }
    return 0;
}