题解 - CF662C Binary Table

题解 - C F   662 C   B i n a r y   T a b l e \mathrm{CF\ 662C\ Binary \ Table}

题目意思

  • CF662C Binary Table
  • 有一个 n × m n\times m 的表格,每个元素都是 0 / 1 0/1 ,每次操作可以选择一行或一列,把 0 / 1 0/1 翻转,即把 0 0 换为 1 1 ,把 1 1 换为 0 0 。请问经过若干次操作后,表格中最少有多少个 1 1
  • n 20 , m 1 0 5 n\leq 20,m\leq 10^5

S o l \mathrm{Sol}

  • 前置知识:状压+ f w t fwt
  • 我们首先先考虑 O ( 2 n × m ) O(2^n\times m) 的暴力:因为 n 20 n\leq 20 我们考虑先把每一列压成 0 / 1 0/1 串。然后 O ( m ) O(m) 枚举翻转哪一列,然后再 x o r xor 上枚举的行的反转状态,取 min ( 0 , 1 ) \min(0,1) 就可以了。
  • 我们考虑优化,设 f i f_i 为状态为 i i 的个数, g i g_i 为状态为 i i 下的 min ( 0 , 1 ) \min(0,1) ,那么 a n s i = j   x o r   k = i f j × g k ans_i=\sum_{j\ xor\ k=i} f_j\times g_k 。这个就变成了 f w t fwt 的模板。
  • 时间复杂度: O ( n × 2 n ) O(n\times 2^n)

C o d e \mathrm{Code}

#include <bits/stdc++.h>
#define pb push_back
#define int long long 
using namespace std;

inline int read()
{
	int sum=0,ff=1; char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') ff=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		sum=sum*10+(ch^48),ch=getchar();
	return sum*ff;
}

const int N=21;
const int M=1e5+5;
const int INV=499122177;

int n,m,a[N][M],f[1<<N],g[1<<N],S[M];

inline void fwt_xor(int *a,int op)
{
	for ( int mid=1;mid<(1ll<<n);mid<<=1ll ) 
		for ( int j=0,len=mid<<1;j<=(1ll<<n);j+=len ) 
			for ( int k=j;k<mid+j;k++ )
			{
				int x=a[k],y=a[k+mid];
				a[k]=(x+y);
				a[k+mid]=(x-y);
				if(op==-1)
				{
					a[k]=a[k]>>1;
					a[k+mid]=a[k+mid]>>1;
				}
			}
}

signed main()
{
	n=read();
	m=read();
	for ( int i=1;i<=n;i++ ) 
		for ( int j=1;j<=m;j++ ) 
		{
			scanf("%1d",&a[i][j]);
			if(a[i][j]) S[j]|=(1ll<<i-1);
		}
	for ( int i=1;i<=m;i++ ) f[S[i]]++;
	for ( int i=0;i<(1ll<<n);i++ )
	{
		int gs=__builtin_popcount(i);
		g[i]=min(gs,n-gs);
	}
	fwt_xor(f,1);
	fwt_xor(g,1);
	for ( int i=0;i<(1ll<<n);i++ ) f[i]=f[i]*g[i];
	fwt_xor(f,-1);
	int ans=1e9;
	for ( int i=0;i<(1ll<<n);i++ ) ans=min(ans,f[i]);
	printf("%lld\n",ans);
	return 0;
}
		 

猜你喜欢

转载自blog.csdn.net/wangyiyang2/article/details/105596854