题解:CF1054G New Road Network

posted on 2024-11-07 05:17:52 | under | source

构造,思想是寻找充要条件。

先想想,假如只有一种集合 \(S\) 的限制,记 \(k=|S|\)。由于 \(k\) 个点构成一棵树的充要条件是边数为 \(k-1\)。而且 \(k-1\) 一定是所有情况中最大的边数,所以可以定义 \((u,v)\) 的边权为 \([u\in S\land v\in S]\),那么跑最大生成树即可。

不难发现上述结论可以直接推广到多个集合的情况,因为集合互不干扰,所以定义 \((u,v)\) 边权为满足 \(u,v\) 同时出现的集合个数,跑最大生成树,若达到上界也就是 \(\sum\limits_{i=1}^m max(0,|S_i|-1)\) 的话就是有解,否则无解。

复杂度 \(O(n^2m+n^2\log n^2)\),可以使用 bitset 计算边权,复杂度 \(O(\frac {n^2m}{w}+n^2\log n^2)\)

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 2e3 + 5, M = 4e6 + 5;
int T, n, m, fa[N], sum;
bitset<N> bs[N]; 
char s[N];
struct edge{
	int x, y, w;
}e[M], output[N];

inline int find(int u) {return fa[u] == u ? u : fa[u] = find(fa[u]);}
signed main(){
	cin >> T; 
	while(T--){
		scanf("%d%d", &n, &m);
		sum = 0;
		for(int i = 1; i <= n; ++i) bs[i].reset(), fa[i] = i;
		for(int i = 1; i <= m; ++i){
			scanf("%s", s + 1);
			int sbnjc = 0;
			for(int j = 1; j <= n; ++j)
				if(s[j] == '1') bs[j].set(i), ++sbnjc;
			sum += max(0, sbnjc - 1);
		}
		int tot = 0, tt = 0; 
		for(int i = 1; i <= n; ++i)
			for(int j = i + 1; j <= n; ++j)
				e[++tot] = {i, j, (bs[i] & bs[j]).count()};
		sort(e + 1, e + 1 + tot, [](edge A, edge B){return A.w > B.w;});
		for(int i = 1; i <= tot; ++i){
			int fx = find(e[i].x), fy = find(e[i].y), w = e[i].w;
			if(fx ^ fy) sum -= w, fa[fx] = fy, output[++tt] = {e[i].x, e[i].y};
		}
		if(sum) {puts("NO");}
		else{
			puts("YES");
			for(int i = 1; i < n; ++i) printf("%d %d\n", output[i].x, output[i].y);
			putchar('\n');
		}
	}
	return 0;
}

posted @ 2026-01-15 08:16  Zwi  阅读(1)  评论(0)    收藏  举报