题解 - CF662C Binary Table

25 篇文章 0 订阅
1 篇文章 0 订阅

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

题目意思

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

S o l \mathrm{Sol} Sol

  • 前置知识:状压+ f w t fwt fwt
  • 我们首先先考虑 O ( 2 n × m ) O(2^n\times m) O(2n×m)的暴力:因为 n ≤ 20 n\leq 20 n20我们考虑先把每一列压成 0 / 1 0/1 0/1 串。然后 O ( m ) O(m) O(m)枚举翻转哪一列,然后再 x o r xor xor 上枚举的行的反转状态,取 min ⁡ ( 0 , 1 ) \min(0,1) min(0,1)就可以了。
  • 我们考虑优化,设 f i f_i fi为状态为 i i i的个数, g i g_i gi为状态为 i i i下的 min ⁡ ( 0 , 1 ) \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 ansi=j xor k=ifj×gk。这个就变成了 f w t fwt fwt的模板。
  • 时间复杂度: O ( n × 2 n ) O(n\times 2^n) O(n×2n)

C o d e \mathrm{Code} 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;
}
		 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值