cf1250E(二分图)

题目链接

https://codeforces.com/contest/1250/problem/E

题意

给定 $n$ 个 $01$ 串,可以对其中若干个串进行前后翻转,问用最少的翻转次数,使得这些串两两之间的相似度大于 $k$ 。相似度定义为两个串做同或的 $1$ 的个数

$n\le50,|S|\le 50$

题解

显然一个串只有两种状态,所以要决策将这个串放入哪个集合,使得两两之间不会发生冲突(相似度小于 $k$ ),很容易联想到最小割之类的东西。。。

那么考虑两个串 $S,T$ 之间可能出现的关系

若 $S$ 和 $T$ 冲突且 $S$ 和 $rev(T)$ 也会冲突,那么必然不会存在合法方案

若 $S$ 和 $T$ 冲突且 $S$ 和 $rev(T)$ 不冲突,那么 $S$ 和 $T$ 必然得在同一个集合中,我们考虑将这两个点合并

若 $S$ 和 $T$ 不冲突且 $S$ 和 $rev(T)$ 冲突,那么这两个串必然存在于不同集合中,连边

若 $S$ 和 $T$ 不冲突且 $S$ 和 $rev(T)$ 也不冲突,那么这两个串之间独立

另外,在合并完节点之后需要保证这些点之间的原串是不冲突的,因为同一集合这个限制条件没有传递性

然后考虑当前建的图,对每个联通块,如果不是二分图,那么显然也是不存在合法方案的。如果是二分图,选择点数较少的一侧翻转

然后就没了。。$O(n^2)$ 50ms 200KB 你敢信?




代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
*         ┏┓    ┏┓
*         ┏┛┗━━━━━━━┛┗━━━┓
*         ┃       ┃  
*         ┃   ━    ┃
*         ┃ >   < ┃
*         ┃       ┃
*         ┃... ⌒ ...  ┃
*         ┃ ┃
*         ┗━┓ ┏━┛
*          ┃ ┃ Code is far away from bug with the animal protecting          
*          ┃ ┃ 神兽保佑,代码无bug
*          ┃ ┃           
*          ┃ ┃       
*          ┃ ┃
*          ┃ ┃           
*          ┃ ┗━━━┓
*          ┃ ┣┓
*          ┃ ┏┛
*          ┗┓┓┏━━━━━━━━┳┓┏┛
*           ┃┫┫ ┃┫┫
*           ┗┻┛ ┗┻┛
*/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<set>
#include<bitset>
#include<complex>
#include<cstdlib>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define mid (x+y>>1)
#define eps 1e-8
#define succ(x) (1ll<<(x))
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define NM 55
#define nm 10005
using namespace std;
const double pi=acos(-1);
const int inf=998244353;
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}


struct edge{int t;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
int n,m,_p,f[NM],tot,c[NM];
bool a[NM][NM];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
char str[NM][NM],_str[NM][NM];

bool cmp(char*x,char*y){
int s=0;
inc(i,0,m-1)if(x[i]==y[i])s++;
return s<_p;
}

bool v[NM];
queue<int>q;
int d[NM];
vector<int>cnt[2],ans;
vector<int>vec[NM];


void solve(){
mem(v);mem(d);mem(h);o=e;
ans.clear();
while(!q.empty())q.pop();
n=read();m=read();_p=read();
inc(i,1,n){
scanf("%s",str[i]);
reverse_copy(str[i],str[i]+strlen(str[i]),_str[i]);
}
inc(i,1,n)f[i]=i;
inc(i,1,n)inc(j,i+1,n){
if(cmp(str[i],str[j])){
if(cmp(str[i],_str[j])){printf("-1\n");return;}
a[i][j]=a[j][i]=true;
}else{
a[i][j]=a[j][i]=false;
if(cmp(str[i],_str[j]))
f[find(i)]=find(j);
}
}
tot=0;
inc(i,1,n)if(find(i)==i)c[i]=++tot;
inc(i,1,tot)vec[i].clear();
inc(i,1,n)vec[c[find(i)]].push_back(i);
inc(i,1,tot)for(auto&j:vec[i])for(auto&k:vec[i])if(a[j][k]){printf("-1\n");return;}
inc(i,1,n)inc(j,1,n)if(a[i][j]&&find(i)!=find(j)){
add(c[find(i)],c[find(j)]);
}
inc(i,1,tot)if(!v[i]){
v[i]++;cnt[0].clear();cnt[1].clear();
q.push(i);
while(!q.empty()){
int t=q.front();q.pop();
for(auto&j:vec[t])cnt[d[t]].push_back(j);
link(t)if(!v[j->t]){
v[j->t]++;
d[j->t]=d[t]^1;
q.push(j->t);
}else if(d[j->t]==d[t]){printf("-1\n");return;}
}
if(cnt[0].size()<cnt[1].size())
for(auto&j:cnt[0])ans.push_back(j);
else
for(auto&j:cnt[1])ans.push_back(j);
}
printf("%d\n",(int)ans.size());
for(auto&j:ans)printf("%d ",j);putchar('\n');
}


int main(){
int _=read();while(_--)solve();
return 0;
}