题目大意:给定一张 $n$ 个点 $m$ 条边的无向图,每条边为黑色或白色,求一棵生成树使得其中恰好有 $k$ 条白边,输出任意一种方案。$n \leq 2 \times 10^4, m \leq 10^5$
优先加黑边,可以得到哪些白边是必须加的,于是先把必须加的白边加上,然后随意找其他的白边填满 $k$ 条,再添黑边即可。
#include <cstdio>
#include <algorithm>
const int N = 20005, M = 100005;
struct Edge {
int u, v, c;
bool f;
} e[M];
int fa[N], n, m, k;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
bool cmp1(Edge a, Edge b) {
return a.c > b.c;
}
bool cmp2(Edge a, Edge b) {
return a.c < b.c;
}
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
int main() {
n = read(), m = read(), k = read();
for (int i = 1; i <= m; ++i)
e[i].u = read(), e[i].v = read(), e[i].c = read();
std::sort(e + 1, e + m + 1, cmp1);
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i) {
int fx = find(e[i].u), fy = find(e[i].v);
if (fx != fy) {
fa[fx] = fy;
if (e[i].c == 0) e[i].f = 1;
}
}
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i)
if (e[i].f) fa[find(e[i].u)] = find(e[i].v), ++cnt1, ++cnt2;
std::sort(e + 1, e + m + 1, cmp2);
for (int i = 1; i <= m; ++i) {
int fx = find(e[i].u), fy = find(e[i].v);
if (fx != fy) {
if (e[i].c == 0) {
if (cnt1 == k) continue;
++cnt1;
}
fa[fx] = fy, e[i].f = 1, ++cnt2;
}
}
if (cnt1 != k || cnt2 != n - 1) puts("no solution");
else {
for (int i = 1; i <= m; ++i)
if (e[i].f) printf("%d %d %d\n", e[i].u, e[i].v, e[i].c);
}
return 0;
}
时间复杂度 $O(m \log m)$