bzoj 3624 免费道路
题目大意:
N 个村庄,由 M 条道路连接 其中一些道路是鹅卵石路,而其它道路是水泥路
求一个方案使保留尽可能少的道路,但是两个不同的村庄之间都应该由一条且仅由一条免费道路的路径连接且刚好保留K条鹅卵石路
思路:
并查集
先将所有水泥路都加入并查集中
然后找到那些必须被加入的鹅卵石路
再补上其他的鹅卵石路达到k个
最后加上其他水泥路
因为用并查集实现所以最后答案为最小
(写了三个merge贼辣鸡以及一堆细节见代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 #define ll long long 10 #define MAXN 500100 11 #define inf 2139062143 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 int n,m,k,fa[MAXN],sz,cnt; 21 struct edge {int u,v,c;}e[MAXN]; 22 int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);} 23 //普通合并 24 void merge(int a,int b) 25 { 26 int x=find(a),y=find(b); 27 if(x!=y) sz--,fa[x]=y; 28 } 29 //带输出及填k个鹅卵石路合并 30 void Merge(int a,int b,int &z) 31 { 32 int x=find(a),y=find(b); 33 if(x==y) return ; 34 if(!z&&k>0) {fa[x]=y,k--;printf("%d %d %d\n",a,b,z);z=1;} 35 else if(z) {fa[x]=y;printf("%d %d %d\n",a,b,z);} 36 } 37 //记录需要的鹅卵石路及填k个鹅卵石路合并 38 void MergE(int a,int b,int &z) 39 { 40 int x=find(a),y=find(b); 41 if(x==y||k==0) return ; 42 fa[x]=y,k--,z=-1,sz--; 43 } 44 int main() 45 { 46 n=read(),m=read(),k=read(),sz=n; 47 for(int i=1;i<=n;i++) fa[i]=i; 48 for(int i=1;i<=m;i++) {e[i].u=read(),e[i].v=read(),e[i].c=read(),cnt+=e[i].c;merge(e[i].u,e[i].v);} 49 if(m-cnt<k||sz>1||n==1) {puts("no solution");return 0;}//鹅卵石路不够k个或者所有路都无法把所有点联通 50 for(int i=1;i<=n;i++) fa[i]=i;sz=n; 51 for(int i=1;i<=m;i++) if(e[i].c) merge(e[i].u,e[i].v); 52 //求出必须的鹅卵石路 53 for(int i=1;i<=m;i++) 54 if(!e[i].c) MergE(e[i].u,e[i].v,e[i].c); 55 if(sz>1) {puts("no solution");return 0;}//必须的鹅卵石路比k个多 56 for(int i=1;i<=n;i++) fa[i]=i; 57 for(int i=1;i<=m;i++) 58 if(e[i].c<0) merge(e[i].u,e[i].v); 59 //求出其他鹅卵石路 60 for(int i=1;i<=m;i++) 61 if(!e[i].c) MergE(e[i].u,e[i].v,e[i].c); 62 if(k) {puts("no solution");return 0;}//鹅卵石路形成的无环图达不到k个 63 for(int i=1;i<=m;i++) 64 if(e[i].c<0) printf("%d %d 0\n",e[i].u,e[i].v); 65 for(int i=1;i<=m;i++) 66 if(e[i].c>0) Merge(e[i].u,e[i].v,e[i].c); 67 }