一、题目
二、解法
观察到
n
n
n很小,我们肯定要从这里入手,设
i
i
i列的状态压缩为
s
i
s_i
si,我们先枚举行的翻转选择
x
x
x,那么列翻转后的状态是
y
i
=
s
i
⊕
x
y_i=s_i\oplus x
yi=si⊕x,设
f
i
f_i
fi为二进制
i
i
i最少有多少个
1
1
1(可翻转),那么答案就是:
∑
i
=
1
m
f
s
i
⊕
x
\sum_{i=1}^m f_{s_i\oplus x}
i=1∑mfsi⊕x我们枚举这个
s
i
s_i
si和
y
i
y_i
yi,那么
x
x
x就不需要了,设
q
i
q_i
qi为有多少列的初始状态是
i
i
i:
∑
s
=
0
2
n
−
1
∑
y
=
0
2
n
−
1
[
s
⊕
y
=
x
]
f
y
×
q
s
\sum_{s=0}^{2^n-1}\sum_{y=0}^{2^n-1}[s\oplus y=x]f_y\times q_s
s=0∑2n−1y=0∑2n−1[s⊕y=x]fy×qs改一种写法,就会清晰很多:
a
n
s
(
x
)
=
∑
s
⊕
y
=
x
f
y
×
q
s
ans(x)=\sum_{s\oplus y=x}f_y\times q_s
ans(x)=s⊕y=x∑fy×qs直接搞
x
o
r
xor
xor卷积就可以了,时间复杂度
O
(
n
2
n
)
O(n2^n)
O(n2n)
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 110005;
#define int long long
int read()
{
int num=0,flag=1;
char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,m,lim,a[25][M],s[M],f[M*10],g[M*10];
void fwt(int *a,int n,int op)
{
for(int i=1; i<n; i<<=1)
for(int p=i<<1,j=0; j<n; j+=p)
for(int k=0; k<i; k++)
{
int x=a[j+k],y=a[i+j+k];
a[j+k]=x+y;
a[i+j+k]=x-y;
if(op==-1)
{
a[j+k]=a[j+k]/2;
a[i+j+k]=a[i+j+k]/2;
}
}
}
signed main()
{
n=read();
m=read();
lim=1<<n;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
scanf("%1lld",&a[i][j]);
for(int i=1; i<=m; i++)
for(int j=1; j<=n; j++)
s[i]|=(a[j][i]<<j-1);
for(int i=1; i<=m; i++)
f[s[i]]++;
for(int i=0; i<lim; i++)
g[i]=g[i>>1]+(i&1);
for(int i=0; i<lim; i++)
g[i]=min(g[i],n-g[i]);
fwt(f,lim,1);
fwt(g,lim,1);
for(int i=0; i<lim; i++) f[i]=f[i]*g[i];
fwt(f,lim,-1);
int ans=1e9;
for(int i=0; i<lim; i++) ans=min(ans,f[i]);
printf("%lld\n",ans);
}