bzoj 3624 [Apio2008]免费道路

http://www.elijahqi.win/archives/3685
题目描述
新亚(New Asia)王国有 N 个村庄,由 M 条道路连接。其中一些道路是鹅卵石路,而其它道路是水泥路。保持道路免费运行需要一大笔费用,并且看上去 王国不可能保持所有道路免费。为此亟待制定一个新的道路维护计划。

国王已决定保持尽可能少的道路免费,但是两个不同的村庄之间都应该一条且仅由一条 且仅由一条免费道路的路径连接。同时,虽然水泥路更适合现代交通的需 要,但国王也认为走在鹅卵石路上是一件有趣的事情。所以,国王决定保持刚好 K 条鹅卵石路免费。

举例来说,假定新亚王国的村庄和道路如图 3(a)所示。如果国王希望保持两 条鹅卵石路免费,那么可以如图 3(b)中那样保持道路(1, 2)、(2, 3)、(3, 4)和(3, 5) 免费。该方案满足了国王的要求,因为:(1)两个村庄之间都有一条由免费道 路组成的路径;(2)免费的道路已尽可能少;(3)方案中刚好有两条鹅卵石道路 (2, 3)和(3, 4)

图 3: (a)新亚王国中村庄和道路的一个示例。实线标注的是水泥路,虚线标注 的是鹅卵石路。(b)一个保持两条鹅卵石路免费的维护方案。图中仅标出了免 费道路。

给定一个关于新亚王国村庄和道路的述以及国王决定保持免费的鹅卵石 道路数目,写一个程序确定是否存在一个道路维护计划以满足国王的要求,如果 存在则任意输出一个方案。

输入输出格式
输入格式:

输入第一行包含三个由空格隔开的整数:

N,村庄的数目(1≤N≤20,000);

M,道路的数目(1≤M≤100,000);

K,国王希望保持免费的鹅卵石道路数目(0≤K≤N - 1)。

此后 M 行述了新亚王国的道路,编号分别为 1 到 M。第(i+1)行述了第 i 条 道路的情况。用 3 个由空格隔开的整数述:

ui 和 vi,为第 i 条道路连接的两个村庄的编号,村庄编号为 1 到 N;

ci,表示第 i 条道路的类型。ci = 0 表示第 i 条道路是鹅卵石路,ci = 1 表 示第 i 条道路是水泥路。

输入数据保证一对村庄之间至多有一条道路连接

输出格式:

如果满足国王要求的道路维护方案不存在,你的程序应该在输出第一行打印 no solution。 否则,你的程序应该输出一个符合要求的道路维护方案,也就是保持免费的 道路列表。按照输入中给定的那样输出免费的道路。如果有多种合法方案,你可 以任意输出一种。

输入输出样例
输入样例#1: 复制

5 7 2
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1
输出样例#1: 复制

3 2 0
4 3 0
5 3 1
1 2 1
求出必须用的鹅卵石路 和水泥路

全选水泥路 看有哪些鹅卵石路必须添加 求水泥路同理反过来即可

然后判断是否合法 如果合法则继续把剩下的边尝试添加 最后判断图是否联通

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if(T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=2e4+10;
const int M=1e5+10;
struct node{
    int x,y,op;
}eg[M],ans[N];
int fa[N],tot,n,m,k;
inline int find(int x){
    while(x!=fa[x]) x=fa[x]=fa[fa[x]];return x;
}
inline void merge(int x,int y){
    if (find(x)==find(y)) return;
    fa[find(x)]=find(y);
}
int main(){
    freopen("bzoj3624.in","r",stdin);
    n=read();m=read();k=read();
    for (int i=1;i<=n;++i) fa[i]=i;
    for (int i=1;i<=m;++i){
        eg[i].x=read();eg[i].y=read();eg[i].op=read();
        if (eg[i].op) merge(eg[i].x,eg[i].y);
    }int tot1=0;
    for (int i=1;i<=m;++i){
        if (eg[i].op) continue;
        if (find(eg[i].x)!=find(eg[i].y)) ans[++tot]=eg[i],merge(eg[i].x,eg[i].y);
        if (tot>k) {puts("no solution");return 0;}
    }int cnt=tot;
    for (int i=1;i<=n;++i) fa[i]=i;
    for (int i=1;i<=m;++i)
        if(!eg[i].op) merge(eg[i].x,eg[i].y);
    for (int i=1;i<=m;++i){
        if (!eg[i].op) continue;
        if (find(eg[i].x)!=find(eg[i].y)) ans[++tot]=eg[i],merge(eg[i].x,eg[i].y),++tot1;
        if (tot1>n-1-k) {puts("no solution");return 0;}
    }
    for (int i=1;i<=n;++i) fa[i]=i;
    for (int i=1;i<=tot;++i) merge(ans[i].x,ans[i].y);
    for (int i=1;i<=m;++i){
        if (find(eg[i].x)!=find(eg[i].y)&&cnt<k&&!eg[i].op) ++cnt,ans[++tot]=eg[i],merge(eg[i].x,eg[i].y);
        if (find(eg[i].x)!=find(eg[i].y)&&tot1<n-k-1&&eg[i].op) ++tot1,ans[++tot]=eg[i],merge(eg[i].x,eg[i].y);
    }
    if (tot!=n-1) {puts("no solution");return 0;}
    for (int i=1;i<=tot;++i) printf("%d %d %d\n",ans[i].x,ans[i].y,ans[i].op);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值