很好的题目
看到列很多但是行很少,我们显然想到状压行,考虑枚举一个
Mask
M
a
s
k
表示行的翻转状态,第i位是1表示翻转了第i行,反之亦然
这样枚举完了以后,设第i列原来的状态是
mask
m
a
s
k
,那么现在的状态就是
Mask⨁mask
M
a
s
k
⨁
m
a
s
k
,这时我们考虑是否需要翻转第i列的时候,只要考虑
Mask⨁mask
M
a
s
k
⨁
m
a
s
k
和
!(Mask⨁mask)
!
(
M
a
s
k
⨁
m
a
s
k
)
中哪个1比较少就可以了
形式化的,记
F(mask)
F
(
m
a
s
k
)
表示原来的m列中有多少列的值是
mask
m
a
s
k
,记
G(mask)
G
(
m
a
s
k
)
表示如果考虑过行的翻转后这列的状态是
mask
m
a
s
k
,这列最终最少能有多少个1,即
G(mask)=min(buitin−popcount(mask),n−builtin−popcount(mask))
G
(
m
a
s
k
)
=
m
i
n
(
b
u
i
t
i
n
−
p
o
p
c
o
u
n
t
(
m
a
s
k
)
,
n
−
b
u
i
l
t
i
n
−
p
o
p
c
o
u
n
t
(
m
a
s
k
)
)
我们的目标是计算:
然后我们很开心的发现这样的复杂度是 O(22n) O ( 2 2 n ) ,会超时
尝试重写上面的式子,记 res=Mask⨁mask r e s = M a s k ⨁ m a s k
考虑到异或的优良性质,中括号内的等式内的元素是可以换顺序的,即
还不够明显,换个写法
很好,这就是一个异或卷积,所以可以FWT
这样总复杂度为 O(2nlog(2n)) O ( 2 n l o g ( 2 n ) ) ,即 O(n2n) O ( n 2 n )
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;
const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=3.14159265;
inline int getint()
{
char ch;int res;bool f;
while (!isdigit(ch=getchar()) && ch!='-') {}
if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
while (isdigit(ch=getchar())) res=res*10+ch-'0';
return f?res:-res;
}
int n,m,len;
char s[22][100048];
LL a[2000048],b[2000048];
inline void FWT(LL c[],int fl)
{
int clen,j,k;
for (clen=2;clen<=len;clen<<=1)
for (j=0;j<len;j+=clen)
for (k=j;k<j+(clen>>1);k++)
{
LL tmp1=c[k],tmp2=c[k+(clen>>1)];
if (fl==1)
c[k]=tmp1+tmp2,c[k+(clen>>1)]=tmp1-tmp2;
else
c[k]=((tmp1+tmp2)>>1),c[k+(clen>>1)]=((tmp1-tmp2)>>1);
}
}
inline void calc_FWT()
{
len=(1<<n);
FWT(a,1);FWT(b,1);
for (register int i=0;i<=(1<<n)-1;i++) a[i]*=b[i];
FWT(a,-1);
}
int main ()
{
int i,j,Mask;
n=getint();m=getint();
for (i=1;i<=n;i++) scanf("%s",s[i]+1);
for (j=1;j<=m;j++)
{
Mask=0;
for (i=1;i<=n;i++) Mask|=((s[i][j]-'0')<<(i-1));
a[Mask]++;
}
for (Mask=0;Mask<=(1<<n)-1;Mask++)
b[Mask]=min(__builtin_popcount(Mask),n-__builtin_popcount(Mask));
calc_FWT();
LL ans=LINF;
for (i=0;i<=(1<<n)-1;i++) ans=min(ans,a[i]);
printf("%lld\n",ans);
return 0;
}