题解[CF1054G New Road Network]

题意描述

Link

\(kals\)已经翻得很好了~~

Sol

\(S_a\)\(a\)点所在集合的集合,\(S_b\)\(b\)点所在集合的集合,每次连边\((a,b)\) ,边权为\(S_a\)\(S_b\)的大小,这样建出一张完全图来,跑最大生成树(\(why?\)感性理解:如果我连交集最多、最有可能的、最有希望的连边方式都不可行,那就没有可行的了)。做法看似简单,证明太难 ,实现上有一些难点(如\(bitset\)),解释一下。

输入

for(int i=1;i<=m;i++){
	char c[N];
	scanf("%s",c+1);
	for(int j=1;j<=n;j++)
		a[j][i]=c[j]-'0',sum[i]+=a[j][i];
}

\(a[j][i]\)表示第\(j\)个点是否存在于第\(i\)个集合里,\(sum[i]\)表示集合\(i\)里有几个点。

连边

for(int i=1;i<n;i++)
	for(int j=i+1;j<=n;j++)
		e[++cnt].st=i,e[cnt].ed=j,e[cnt].v=(int)(a[i]&a[j]).count();

\(a[i]\) & \(a[j]\)表示(\(i\)所在的集合的集合)与(\(j\)所在的集合的集合)的交集,\(.count()\) 表示交集中\(1\)的个数,也就是大小。

判定

for(int i=1;i<=m;i++){
	int s=0;
	for(int j=1;j<n;j++) s+=(a[ans[j].st][i]&&a[ans[j].ed][i]);
  //ans[j].st和ans[j].ed同在集合i里
  //s为集合中边的数量
  //s=点的数量-1
	if(s!=sum[i]-1){
		flag=0;
		break;
	}
  //不符合输入,构造不出来
}

Code

#include<bits/stdc++.h>
#define M (4000010)
#define N (2010)
using namespace std;
struct xbk{int st,ed,v;}e[M],ans[N];
int n,m,T,sum[N],fa[N];
bitset<N>a[N];
inline int read(){
	int w=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') ch=getchar();
	while(ch>='0'&&ch<='9'){
		w=(w<<3)+(w<<1)+(ch^48);
		ch=getchar();
	}
	return w;
}
inline bool cmp(xbk a,xbk b){return a.v>b.v;}
inline int mfind(int x){
	if(fa[x]!=x) return fa[x]=mfind(fa[x]);
	return fa[x];
}
int main(){
	T=read();
	while(T--){
		n=read(),m=read();
		int cnt=0,tot=0;
		memset(sum,0,sizeof(sum));
		for(int i=1;i<=n;i++) a[i].reset(),fa[i]=i;
		for(int i=1;i<=m;i++){
			char c[N];
			scanf("%s",c+1);
			for(int j=1;j<=n;j++)
				a[j][i]=c[j]-'0',sum[i]+=a[j][i];
		}
		for(int i=1;i<n;i++)
			for(int j=i+1;j<=n;j++)
				e[++cnt].st=i,e[cnt].ed=j,e[cnt].v=(int)(a[i]&a[j]).count();
		sort(e+1,e+1+cnt,cmp);
		for(int i=1;i<=cnt;i++){
			int x=mfind(e[i].st),y=mfind(e[i].ed);
			if(x==y) continue;
			fa[x]=y;
			ans[++tot].st=e[i].st,ans[tot].ed=e[i].ed;
			if(tot==n-1) break;
		}
		bool flag=1;
		for(int i=1;i<=m;i++){
			int s=0;
			for(int j=1;j<n;j++) s+=(a[ans[j].st][i]&&a[ans[j].ed][i]);
			if(s!=sum[i]-1){
				flag=0;
				break;
			}
		}
		if(!flag) puts("NO");
		else{
			puts("YES");
			for(int i=1;i<n;i++) printf("%d %d\n",ans[i].st,ans[i].ed);
		}
	}
	return 0;
}

完结撒花❀

posted @ 2021-02-22 19:54  xxbbkk  阅读(62)  评论(0编辑  收藏  举报