(最小生成树)公路修建问题
\(二分 + 最小生成树\)
题意
\(每两个点有长边和短边,要求生成最小生成树,并且长边的次数不少于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;
}