题目描述
给定一个
的
矩阵,可以选择任意行或列进行操作,被选到的行
值取反,询问操作后矩阵最少含
的数量。
分析:
由于行很少,所以我们考虑对每一列讨论。
设
为状态为
的列的数目。
为状态
最少
的个数,显然
,
表示
的
的个数,
表示把
取反。
为给行取反的状态,此时,每一个
变成了
,有
直接fwt解决即可。
代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#define LL long long
const int maxn=1048580;
using namespace std;
LL n,m;
LL g[maxn],f[maxn],h[maxn];
bool a[21][100007];
char ch;
LL calc(LL x)
{
LL sum=0;
for (LL i=x;i;i-=i&(-i)) sum++;
return sum;
}
void fwt(LL *a,LL l,LL r)
{
if (l==r) return;
LL n=(r-l+1)/2,mid=l+n;
fwt(a,l,mid-1);
fwt(a,mid,r);
for (LL i=l;i<mid;i++)
{
LL u=a[i],v=a[i+n];
a[i]=u+v;
a[i+n]=u-v;
}
}
void dwt(LL *a,LL l,LL r)
{
if (l==r) return;
LL n=(r-l+1)/2,mid=l+n;
dwt(a,l,mid-1);
dwt(a,mid,r);
for (LL i=l;i<mid;i++)
{
LL u=a[i],v=a[i+n];
a[i]=(u+v)/2;
a[i+n]=(u-v)/2;
}
}
int main()
{
scanf("%lld%lld",&n,&m);
for (LL i=1;i<=n;i++)
{
scanf("\n");
for (LL j=1;j<=m;j++)
{
scanf("%c",&ch);
a[i][j]=(ch=='1');
}
}
for (LL i=0;i<(1<<n);i++)
{
LL c=calc(i);
g[i]=min(c,n-c);
}
for (LL j=1;j<=m;j++)
{
LL s=0;
for (LL i=1;i<=n;i++) s=(s<<1)+a[i][j];
f[s]++;
}
fwt(f,0,(1<<n)-1);
fwt(g,0,(1<<n)-1);
for (LL i=0;i<(1<<n);i++) h[i]=f[i]*g[i];
dwt(h,0,(1<<n)-1);
LL ans=0x3f3f3f3f;
for (LL i=0;i<(1<<n);i++) ans=min(ans,h[i]);
printf("%lld",ans);
}