[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\n",pt[i][1],pt[i][2],pt[i][3]); 85 return 0; 86 no: 87 printf("no solution\n"); 88 return 0; 89 }