网络稳定性

[蓝桥杯 2023 省 A] 网络稳定性

题目描述

有一个局域网,由 n n n 个设备和 m m m 条物理连接组成,第 i i i 条连接的稳定性为 w i w_i wi

对于从设备 A A A 到设备 B B B 的一条经过了若干个物理连接的路径,我们记这条路径的稳定性为其经过所有连接中稳定性最低的那个。

我们记设备 A A A 到设备 B B B 之间通信的稳定性为 A A A B B B 的所有可行路径的稳定性中最高的那一条。

给定局域网中的设备的物理连接情况,求出若干组设备 x i x_i xi y i y_i yi 之间的通信稳定性。如果两台设备之间不存在任何路径,请输出 − 1 -1 1

输入格式

输入的第一行包含三个整数 n , m , q n,m,q n,m,q,分别表示设备数、物理连接数和询问数。

接下来 m m m 行,每行包含三个整数 u i , v i , w i u_i,v_i,w_i ui,vi,wi,分别表示 u i u_i ui v i v_i vi 之间有一条稳定性为 w i w_i wi 的物理连接。

接下来 q q q 行,每行包含两个整数 x i , y i x_i,y_i xi,yi,表示查询 x i x_i xi y i y_i yi 之间的通信稳定性。

输出格式

输出 q q q 行,每行包含一个整数依次表示每个询问的答案。

样例 #1

样例输入 #1

5 4 3
1 2 5
2 3 6
3 4 1
1 4 3
1 5
2 4
1 3

样例输出 #1

-1
3
5

提示

【评测用例规模与约定】

对于 30 % 30 \% 30% 的评测用例, n , q ≤ 500 n,q \leq 500 n,q500 m ≤ 1000 m \leq 1000 m1000

对于 60 % 60 \% 60% 的评测用例, n , q ≤ 5000 n,q \leq 5000 n,q5000 m ≤ 10000 m \leq 10000 m10000

对于所有评测用例, 2 ≤ n , q ≤ 1 0 5 2 \leq n,q \leq 10^5 2n,q105 1 ≤ m ≤ 3 × 1 0 5 1 \leq m \leq 3 \times 10^5 1m3×105 1 ≤ u i , v i , x i , y i ≤ n 1 \leq u_i,v_i,x_i,y_i \leq n 1ui,vi,xi,yin 1 ≤ w i ≤ 1 0 6 1 \leq w_i \leq 10^6 1wi106 u i ≠ v i u_i \neq v_i ui=vi x i ≠ y i x_i \neq y_i xi=yi

PS: 算是非常难的题了, CF中至少2000分
首先路径答案只算最小值,并且要让他最大,想到最大生成树, 然后枚举按权值降序排序的边, 如果有询问查询的点分别在这两个连通块之间则答案就是这条边的权值, 将查询挂在每个连通块父亲上,通过启发式合并暴力转移未被处理的查询点对
时间复杂度O(mlog(m)(边排序) + m(log(q))(启发式合并)
其他乱七八糟的做法比如Kruskal重构树啥的根本没必要,大炮打蚊子

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
#define endl '\n'
#define debug(x) cout << __FUNCTION__ << ": " << #x " = " << (x) << endl
using ll = long long;

const int maxn = 1e6 + 10;
const int maxm = 2e6 + 10;
const ll INF = 3e18;
const int mod = 998244353;
int n, m, k;
int p[maxn];
int find(int x) {
    if (p[x] != x) return p[x] = find(p[x]);
    return p[x];
}

struct Edge {
    int u, v, w;
    bool operator < (const Edge& oth) {
        return w > oth.w;
    }
};

void kkkyyyhhh() {
    int q;
    cin >> n >> m >> q;
    using PII = pair<int, int>;
    vector<vector<PII>> g(n + 1), query(n + 1);
    vector<Edge> edges;
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
        edges.push_back({u, v, w});
    }
    sort(edges.begin(), edges.end());
    vector<int> ans(q + 1);
    for (int i = 1; i <= q; i++) {
        int u, v;
        cin >> u >> v;
        query[u].push_back({v, i});
        query[v].push_back({u, i});
    }
    for (int i = 1; i <= n; i++) p[i] = i;
    for (auto [u, v, w] : edges) {
        int pa = find(u), pb = find(v);
        if (pa == pb) continue;
        if (query[pa].size() > query[pb].size()) swap(pa, pb); // 这就是启发式合并,合并两个集合时只暴力枚举小的集合,注释掉就超时了
        for (auto [v, index] : query[pa]) {
            if (find(v) == pb) ans[index] = w;
            else query[pb].push_back({v, index});
        }
        p[pa] = pb; 
    }
    for (int i = 1; i <= q; i++) {
        if (!ans[i]) ans[i] = -1;
        cout << ans[i] << '\n';
    }
}       

signed main() {
    IOS;
    int w_ = 1;
   // cin >> w_;
    while (w_--) kkkyyyhhh();
    return 0;
}


  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值