BZOJ 3624: [Apio2008]免费道路

题目大意

给定一张 n 20000 个点 m 100000 条边的无向图,每条边为黑色或白色,求一棵生成树使得其中恰好有 k 条黑色的边,输出任意一种方案。


一开始想优先加黑边做生成树,如果超过 k 条再考虑怎么去掉,但断边的话好像要用到LCT……

如果优先加白边,就可以得到哪些黑边是必须加的。

那么先把必须加的黑边加上,然后随意找其他的黑边填满 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;
}

猜你喜欢

转载自blog.csdn.net/Milkyyyyy/article/details/81838076
今日推荐