APIO2008 洛谷P3623 免费道路
题目传送门:https://www.luogu.org/recordnew/show/6743108
题解:写3次并查集,首先不妨留下所有水泥路,添加石子路,那么添加的石子路一定在答案内,留下所有石子路也一样,最后再添加其他边是等价的,注意k值即可,注意要判断好几次无解的情况。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #define MN 20005 4 #define MM 100005 5 using namespace std; 6 int n,m,k,fa[MN],u[MM],v[MM],c[MM],ans[MM],cnt; 7 bool used[MM]; 8 int ff(int x){return fa[x]==x?x:fa[x]=ff(fa[x]);} 9 bool check(){ 10 int tmp=ff(1); 11 for(int i=2;i<=n;i++) 12 if(ff(i)!=tmp) return false; 13 return true; 14 } 15 bool work1(){ 16 for(int i=1;i<=n;i++) fa[i]=i; 17 for(int i=1;i<=m;i++) 18 if(c[i]){ 19 int x=ff(u[i]),y=ff(v[i]); 20 if(x!=y) fa[y]=x; 21 } 22 for(int i=1;i<=m;i++) 23 if(!c[i]){ 24 int x=ff(u[i]),y=ff(v[i]); 25 if(x!=y) fa[y]=x,ans[++cnt]=i,used[i]=true; 26 } 27 if(k<cnt) return false; 28 else k-=cnt; 29 if(!check()) return false; 30 return true; 31 } 32 bool work2(){ 33 for(int i=1;i<=n;i++) fa[i]=i; 34 for(int i=1;i<=m;i++) 35 if(!c[i]){ 36 int x=ff(u[i]),y=ff(v[i]); 37 if(x!=y) fa[y]=x; 38 } 39 for(int i=1;i<=m;i++) 40 if(c[i]){ 41 int x=ff(u[i]),y=ff(v[i]); 42 if(x!=y) fa[y]=x,ans[++cnt]=i,used[i]=true; 43 } 44 if(!check()) return false; 45 return true; 46 } 47 bool work3(){ 48 for(int i=1;i<=n;i++) fa[i]=i; 49 for(int i=1;i<=cnt;i++){ 50 int x=ff(u[ans[i]]),y=ff(v[ans[i]]); 51 if(x!=y) fa[y]=x; 52 else return false; 53 } 54 for(int i=1;i<=m;i++){ 55 if(!k) break; 56 if(!used[i]&&!c[i]){ 57 int x=ff(u[i]),y=ff(v[i]); 58 if(x!=y) fa[y]=x,k--,ans[++cnt]=i; 59 } 60 } 61 if(k) return false; 62 for(int i=1;i<=m;i++) 63 if(!used[i]&&c[i]){ 64 int x=ff(u[i]),y=ff(v[i]); 65 if(x!=y) fa[y]=x,ans[++cnt]=i; 66 } 67 if(!check()) return false; 68 return true; 69 } 70 int main() 71 { 72 scanf("%d%d%d",&n,&m,&k); 73 for(int i=1;i<=m;i++) scanf("%d%d%d",&u[i],&v[i],&c[i]); 74 if(!work1()){puts("no solution");return 0;} 75 if(!work2()){puts("no solution");return 0;} 76 if(!work3()){puts("no solution");return 0;} 77 for(int i=1;i<=cnt;i++) 78 printf("%d %d %d\n",u[ans[i]],v[ans[i]],c[ans[i]]); 79 return 0; 80 }