bzoj3624 [Apio2008]免费道路

本文介绍了一种通过两遍Kruskal算法解决特定生成树问题的方法。目标是在图中找到一棵包含特定数量特殊边的生成树。算法首先优先考虑非特殊边以确定必须选择的边,然后再次运行Kruskal算法确保正确数量的特殊边被包含。

Description

这里写图片描述

Input

这里写图片描述

OutPut

这里写图片描述

Sample Input

5 7 2
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1

Sample Output

3 2 0
4 3 0
5 3 1
1 2 1

Solution

失踪人口回归。
题目大意:求图一棵生成树,使得这棵树里恰好有 k 条特殊边。
两遍 kruskal ,第一遍优先加入非特殊边,预处理出有哪些边是非选不可的。第二遍先加入 k 条特殊边,再加入非特殊边。判断 no solution时细节较多,详见代码。

#include<bits/stdc++.h>
using namespace std;

#define N 200001
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drp(i, a, b) for (int i = a; i >= b; i--)
#define No { puts("no solution"); return 0; }

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

inline void write(int x) {
    if (!x) { putchar('0'); return; } if (x < 0) putchar('-'), x = -x;
    char buf[30] = ""; int top = 0; while (x) buf[++top] = x % 10 + '0', x /= 10; while (top) putchar(buf[top--]);
}

int n, m, K;
struct edgeType {
    int u, v, w; bool chos;
    void outPut() { if (!chos) return; write(u), putchar(' '), write(v), putchar(' '), write(w), puts(""); }
}eg[N];
bool cmp(const edgeType& a, const edgeType& b) { return a.w > b.w; }

int fa[20001];
int find(int x) { return fa[x] ? fa[x] = find(fa[x]) : x; }

int main() {
    n = read(), m = read(), K = read(); 
    rep(i, 1, m) eg[i].u = read(), eg[i].v = read(), eg[i].w = read(); sort(eg + 1, eg + 1 + m, cmp);
    int spCnt = 0;
    rep(i, 1, m) {
        int x = find(eg[i].u), y = find(eg[i].v); if (!(x ^ y)) continue;
        fa[x] = y; if (!eg[i].w) eg[i].chos = 1, spCnt++;
    }
    if (spCnt > K) No;
    memset(fa, 0, sizeof fa); int cnt = 0; spCnt = 0;
    rep(i, 1, m) if (eg[i].chos) { fa[find(eg[i].u)] = find(eg[i].v); cnt++; spCnt++; continue; }
    drp(i, m, 1) {
        if (cnt == n - 1) break; if (eg[i].w && spCnt != K) No;
        int x = find(eg[i].u), y = find(eg[i].v); if (!(x ^ y) || eg[i].chos) continue;
        if (!eg[i].w && spCnt < K) fa[x] = y, eg[i].chos = 1, spCnt++, cnt++;
        else if (eg[i].w) fa[x] = y, eg[i].chos = 1, cnt++;
    }
    if (cnt ^ (n - 1)) No;
    rep(i, 1, m) eg[i].outPut();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值