[luogu]P3623 [APIO2008]免费道路

原题链接:P3623 [APIO2008]免费道路

题意

给定一个图(不一定连通),上面有$m$条$0$边和$1$边。

要求选边使图连通,$0$边的数量必须等于$k$。

分析

一开始用很天真的想法加边:先加鹅卵石路,一直加到$k$条。然后再加水泥路,加到满。

然后发现在洛谷上WA了一个点。

仔细分析一下问题:一对点,如果我们加的是鹅卵石路,那么我们本可以用水泥路使这一对点连通,我们现在却使用了一次鹅卵石名额,这样子可能会导致后面鹅卵石名额不足。

那么怎么考虑这个问题呢?

我们发现只有用鹅卵石代替了水泥才会导致名额的冲突,那么我们只要求出有多少名额是必须给的(不给就不能连通的),然后优先给它们名额。

至于剩下的,就先把鹅卵石的名额加到$k$,然后再加水泥路加到满就可以了。

代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N=2e5+1000,M=3e5+1000;
 4 struct edge{
 5     int x,y;
 6 }sn[M],er[M];
 7 int read(){
 8     char c;int num,f=1;
 9     while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
10     while(c=getchar(), isdigit(c))num=num*10+c-'0';
11     return f*num;
12 }
13 int n,m,k,pre[N];
14 int tot1=0,tot2=0,ans=0;
15 int pt[M][4],vis[M];
16 int fid(int x){return (x==pre[x])?x:(pre[x]=fid(pre[x]));}
17 int main()
18 {
19     n=read();m=read();
20     k=read();
21     for(int i=1;i<=m;i++){
22         int u,v,w;
23         u=read();v=read();
24         w=read();
25         if(w==1){
26             sn[++tot1].x=u;
27             sn[  tot1].y=v;
28         }else{
29             er[++tot2].x=u;
30             er[  tot2].y=v;
31         }
32     }
33     if(tot2<k)goto no;
34     int tt=0;
35     for(int i=1;i<=n;i++)pre[i]=i;
36     for(int i=1;i<=tot1;i++){
37         if(fid(sn[i].x)!=fid(sn[i].y)){
38             pre[fid(sn[i].x)]=fid(sn[i].y);
39             tt++;
40         }
41     }
42     int num=0;
43     for(int i=1;i<=tot2;i++){
44         if(fid(er[i].x)!=fid(er[i].y)){
45             pre[fid(er[i].x)]=fid(er[i].y);
46             vis[i]=1;
47             num++;tt++;
48         }
49     }
50     if(tt<n-1||num>k)goto no;
51 
52     for(int i=1;i<=n;i++)pre[i]=i;
53     for(int i=1;i<=tot2;i++){
54         if(vis[i]){
55             pre[fid(er[i].x)]=fid(er[i].y);
56             pt[++ans][1]=er[i].x;
57             pt[  ans][2]=er[i].y;
58             pt[  ans][3]=0;
59             if(ans==k)break;
60         }
61     }
62     for(int i=1;i<=tot2;i++){
63         if(vis[i])continue;
64         if(fid(er[i].x)!=fid(er[i].y)){
65             pre[fid(er[i].x)]=fid(er[i].y);
66             pt[++ans][1]=er[i].x;
67             pt[  ans][2]=er[i].y;
68             pt[  ans][3]=0;
69             if(ans==k)break;
70         }
71     }
72     if(ans<k)goto no;
73     for(int i=1;i<=tot1;i++){
74         if(fid(sn[i].x)!=fid(sn[i].y)){
75             pre[fid(sn[i].x)]=fid(sn[i].y);
76             pt[++ans][1]=sn[i].x;
77             pt[  ans][2]=sn[i].y;
78             pt[  ans][3]=1;
79             if(ans==n-1)break;
80         }
81     }
82     if(ans<n-1)goto no;
83     for(int i=1;i<=ans;i++)
84         printf("%d %d %d
",pt[i][1],pt[i][2],pt[i][3]);
85     return 0;
86 no:
87     printf("no solution
");
88     return 0;
89 }
View Code
原文地址:https://www.cnblogs.com/onglublog/p/10024242.html