[APIO2008]免费道路

题目链接:[APIO2008]免费道路


显然要找一颗生成树。

显然,对于鹅卵石的路,我们可以找到一些必须要加的边,否则会使得路不连通。

怎么找必须加的边呢?先把水泥路加上,看使得连通还需要加的边即可。

所以加上必须加的边之后,再加几条直至k条鹅卵石边。

然后再加上水泥路。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
//#define int long long
using namespace std;
const int N=2e4+10,M=1e5+10;
int n,m,k,num,f[N],u[M],v[M],c[M],vis[M];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
signed main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)	f[i]=i;
	for(int i=1;i<=m;i++)	scanf("%d %d %d",&u[i],&v[i],&c[i]);
	for(int i=1;i<=m;i++)	if(c[i])	f[find(u[i])]=find(v[i]);
	for(int i=1;i<=m;i++)	if(!c[i]){
		int x=find(u[i]),y=find(v[i]);
		if(x!=y)	f[x]=y,num++,vis[i]=1;
	}
	if(num>k)	return puts("no solution"),0;
	for(int i=1;i<=n;i++)	f[i]=i;
	for(int i=1;i<=m;i++)	if(vis[i])	f[find(u[i])]=find(v[i]);
	for(int i=1;i<=m&&num<k;i++)	if(!vis[i]&&!c[i]){
		int x=find(u[i]),y=find(v[i]);
		if(x!=y)	f[x]=y,num++,vis[i]=1;
	}
	if(num!=k)	return puts("no solution"),0;
	for(int i=1;i<=m;i++)	if(c[i]){
		int x=find(u[i]),y=find(v[i]);
		if(x!=y)	f[x]=y,num++,vis[i]=1;
	}
	if(num!=n-1)	return puts("no solution"),0;
	for(int i=1;i<=m;i++)	if(vis[i])	printf("%d %d %d\n",u[i],v[i],c[i]);
	return 0;
}

你可能感兴趣的:(图论,最小生成树)