(最小生成树)公路修建问题

公路修建问题

\(二分 + 最小生成树\)

题意

\(每两个点有长边和短边,要求生成最小生成树,并且长边的次数不少于K.\\求满足该条件的边权最大的值的最小值.\)

思路

\(注意此题不关心最终的边权和最小,它只关心能否全连通,所有Kruskal不用排序,\\我们去二分最大的边权,由于只用关心一条边是否\ge mid所有可以分别枚举长边和短边.\)

代码
#include <bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
inline int lowbit(int x) { return x & (-x); }
#define ll long long
#define ull unsigned long long
#define pb push_back
#define PII pair<int, int>
#define VIT vector<int>
#define x first
#define y second
#define inf 0x3f3f3f3f
const int N = 1e4 + 7, M = 4e4 + 10;
int n, m, k;
int p[N];

struct Edge {
    int a, b, w;
    bool f;
    bool operator < (const Edge &t) const {
        return w < t.w;
    }
}e[M];

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

bool kruskal(int mid) {
    for (int i = 1; i <= n; ++i) p[i] = i;
    int t1 = 0, t2 = 0;
    for (int i = 0; i < m; ++i) {
        if (e[i].f == 0) continue;
        int a = e[i].a, b = e[i].b, w = e[i].w;
        if (w > mid) break;
        a = find(a), b = find(b);
        if (a != b) {
            p[a] = b;
            ++t1;
        }
    }
    if (t1 < k) return false;
    for (int i = 0; i < m; ++i) {
        if (e[i].f) continue;
        int a = e[i].a, b = e[i].b, w = e[i].w;
        if (w > mid) break;
        a = find(a), b = find(b);
        if (a != b) {
            p[a] = b;
            ++t2;
        }
    }
    if (t1 + t2 < n - 1) return false;
    return true;
}

 
int main() {
    //freopen("in.txt", "r", stdin);
    IO;
    cin >> n >> k >> m;
    m--;
    for (int i = 0; i < m; ++i) {
        int a, b, c1, c2;
        cin >> a >> b >> c1 >> c2;
        e[i] = {a, b, c1, 1};
        e[i + m] = {a, b, c2, 0};
    }
    m *= 2;
    sort(e, e + m);
    int l = 1, r = 30000;
    while (l < r) {
        int mid = l + r >> 1;
        if (kruskal(mid)) r = mid;
        else l = mid + 1;
    }
    cout << l << '\n';
    return 0; 
}


posted @ 2021-03-02 21:11  phr2000  阅读(140)  评论(0编辑  收藏  举报