FWT

[CF662C]Binary Table

Posted by Dispwnl on August 23, 2018

题目

题目大意

有一个 n 行 m 列的表格,每个元素都是 0/1 ,每次操作可以选择一行或一列,把 0/1 翻转,即把 0 换为 1 ,把 1 换为 0 。请问经过若干次操作后,表格中最少有多少个 1 。

Example

input

3 4
0110
1010
0111

output

2

题解

发现nn​非常小,考虑状态压缩

用一个小于2n2^n的数来表示行是否翻转,00表示不翻转,11表示翻转

对于一列jj​(也用一个小于2n2^n​的数表示),ta经过ii​状态的行翻转之后的状态为iji\oplus j​(对应部分为11​就翻转,00​就不翻转)

aia_i表示列状态为ii的总列数,bib_i表示列状态为ii这一列最少的11的个数(就是min(num,nnum)min(num,n-num)),ansians_i表示行翻转状态为ii的时候矩阵最少的11的个数

因为有

ij=k\because i\oplus j=k​

jk=i\therefore j\oplus k=i​

所以ansi=jk=iaj×bkans_i=\sum_{j \oplus k=i}a_j\times b_k​

FWTFWT优化一下就好了,复杂度O(2n×n)O(2^n\times n)

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define LL long long
using namespace std;
const int MAX=(1<<20)+1;
int n,m,len,ans=1e9;
LL a[MAX],b[MAX];
string A[21];
void FWT_XOR(LL *A,int op)
{
	for(int i=1;i<len;i<<=1)
	  for(int p=i<<1,j=0;j<len;j+=p)
	    for(int k=0;k<i;++k)
	      {
	      	LL 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]>>=1,A[i+j+k]>>=1;
		  }
}
int main()
{
	scanf("%d%d",&n,&m),len=1<<n;
	for(int i=0;i<n;++i)
	  cin>>A[i];
	for(int x=0,i=0;i<m;++i,++a[x],x=0)
	  for(int j=0;j<n;x+=(A[j++][i]-'0')*(1<<(j-1)));
	for(int i=0;i<len;++i)
	  {
	  	int x=i;
	  	while(x) b[i]+=x%2,x/=2;
	  	b[i]=min(b[i],n-b[i]);
	  }
	FWT_XOR(a,1),FWT_XOR(b,1);
	for(int i=0;i<len;++i)
	  a[i]*=b[i];
	FWT_XOR(a,-1);
	for(int i=0;i<len;++i)
	  ans=min(1ll*ans,a[i]);
	return printf("%d",ans),0;
}