P16237 [蓝桥杯 2026 省 B] 应急布线

题目链接:

P16237 蓝桥杯 2026 省 B 应急布线 - 洛谷

这道题卡了我比较久,主要是第一次学习了并查集这种算法,以及这道题的处理方式


一、问题化简

把每台计算机看作点,每条仍能工作的网线看作无向边。则"通过残存网线或跳线直接或间接连通"即图上的连通。设当前图由 C个连通块组成。

  • 每条应急跳线连接两台计算机,即一条新边。
  • 目标一:用最少的边使全图连通。
  • 目标二:在边数最少的前提下,最小化"单个点上关联的跳线条数"的最大值。

二、目标一:最少跳线数

C个连通块要并成一片,每加一条边最多使连通块数减一,因此至少需要 C−1条边。

另一方面,把 C个连通块依次串成一条链,相邻两块之间连一条跳线,恰好用 C−1条即可全部连通。

故第一问答案为:

ans1=C−1

特别地,当 C=1 时答案均为 0。


三、目标二:均衡分摊

关键是把计数对象从"连通块"换成"线头"。

  • 共需 C−1 条跳线;
  • 每条跳线有两个端点,因此共有 2(C−1)个线头;
  • 线头必须落在具体的计算机上。

由于每个连通块内部已经连通,块内任意一台计算机都可以作为接线点。因此全部 NN 台计算机都是可用插口。

于是问题转化为:

把 K=2(C−1)个线头分配到 N 台计算机上,最小化单台承担数的最大值。

设单台上限为 t,则 NN 台最多容纳 Nt个线头。要容纳全部线头,须:

取满足该式的最小整数:

需要注意,这个值只是下界;只有当它确实可以通过某种布线方案取到时,才是答案。可以构造证明它可达:将各连通块按"内部可用机器数"排布成一棵树(机器数多的块置于内部、只有一台机器的块置于叶位置),再把块所需的线头按其内部机器轮流分配,即不会超过该上限。

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int n, m;
vector<int> fa;                     // 并查集:fa[x] 表示 x 的父结点

// 查找 x 所在集合的代表元,同时进行路径压缩
int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

// 合并 x、y 所在的两个集合
void unite(int a, int b) {
    a = find(a);
    b = find(b);
    if (a == b) return;             // 已属同一集合,无需合并
    fa[a] = b;                      // 将一个代表元挂到另一个代表元之下
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m;

    // 1. 并查集初始化:每个点单独成为一个集合
    fa.resize(n + 1);
    for (int i = 1; i <= n; ++i) fa[i] = i;

    // 2. 读入残存网线并合并
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        unite(u, v);
    }

    // 3. 统计连通块个数:代表元即满足 fa[i] == i 的点
    int comp = 0;
    for (int i = 1; i <= n; ++i)
        if (fa[i] == i) ++comp;

    // 4. 已全连通,无需布线
    if (comp == 1) {
        cout << "0 0\n";
        return 0;
    }

    // 5. 第一问:C 个连通块至少需要 C-1 条边
    long long ans1 = comp - 1;

    // 6. 第二问:每条边两个端点,共 2*(C-1) 个线头,分摊到 n 台机器上
    //    对 n 向上取整,得到单台承担数的下界(且该下界可达到)
    long long heads = 2 * ans1;
    long long ans2 = (heads + n - 1) / n;   // 向上取整的整数写法

    cout << ans1 << ' ' << ans2 << '\n';
    return 0;
}
相关推荐
raindayinrain1 小时前
c++设计模式
开发语言·c++·设计模式
旖旎夜光1 小时前
LeetCode 315: 算右侧小于当前元素的个数(归并排序) —— 题解
c++·学习·算法·leetcode·排序算法
像风一样自由20201 小时前
2.如何用FastAPI把一个Python算法封装成HTTP服务
python·算法·fastapi
D e re k ZH1 小时前
保序回归:从 PAVA 到可并堆的单调约束优化
c++·算法·数据挖掘·回归·kotlin
青少儿编程课堂1 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:字典树前缀树与单词检索统计
c++·python·算法·bfs·信息学竞赛
Nuanyt1 小时前
LeetCode Hot 100 链表专题2 3 题 (Java)
java·笔记·算法·leetcode·链表
海绵宝宝转agent2 小时前
LeetCodehot100题解
java·笔记·学习·算法·leetcode·深度优先
octopus_c2 小时前
C++ 运算符重载详解 && 日期类的实现
开发语言·c++
青少儿编程课堂2 小时前
2026蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:堆排序与优先队列解析
c++·python·算法·bfs·信息学竞赛