很好的题目
看到列很多但是行很少,我们显然想到状压行,考虑枚举一个MaskMask表示行的翻转状态,第i位是1表示翻转了第i行,反之亦然
这样枚举完了以后,设第i列原来的状态是maskmask,那么现在的状态就是Mask⨁maskMask⨁mask,这时我们考虑是否需要翻转第i列的时候,只要考虑Mask⨁maskMask⨁mask和!(Mask⨁mask)!(Mask⨁mask)中哪个1比较少就可以了
形式化的,记F(mask)F(mask)表示原来的m列中有多少列的值是maskmask,记G(mask)G(mask)表示如果考虑过行的翻转后这列的状态是maskmask,这列最终最少能有多少个1,即G(mask)=min(buitin−popcount(mask),n−builtin−popcount(mask))G(mask)=min(buitin−popcount(mask),n−builtin−popcount(mask))
我们的目标是计算:
minMask=02n−1∑mask=02n−1F(mask)∗G(Mask⨁mask)minMask=02n−1∑mask=02n−1F(mask)∗G(Mask⨁mask)
然后我们很开心的发现这样的复杂度是O(22n)O(22n),会超时
尝试重写上面的式子,记res=Mask⨁maskres=Mask⨁mask
minMask=02n−1∑mask=02n−1F(mask)∗G(Mask⨁mask)minMask=02n−1∑mask=02n−1F(mask)∗G(Mask⨁mask)
=minMask=02n−1∑mask=02n−1∑res=02n−1[Mask⨁mask=res]∗F(mask)∗G(res)=minMask=02n−1∑mask=02n−1∑res=02n−1[Mask⨁mask=res]∗F(mask)∗G(res)
考虑到异或的优良性质,中括号内的等式内的元素是可以换顺序的,即
=minMask=02n−1∑mask=02n−1∑res=02n−1[res⨁mask=Mask]∗F(mask)∗G(res)=minMask=02n−1∑mask=02n−1∑res=02n−1[res⨁mask=Mask]∗F(mask)∗G(res)
还不够明显,换个写法
=minMask=02n−1∑mask⨁res=MaskF(mask)∗G(res)=minMask=02n−1∑mask⨁res=MaskF(mask)∗G(res)
很好,这就是一个异或卷积,所以可以FWT
这样总复杂度为O(2nlog(2n))O(2nlog(2n)),即O(n2n)O(n2n)
#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;
}