题解: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;
}

浙公网安备 33010602011771号