【CodeForces 662C】Binary Table

#Description
给定一个n*m的01矩阵,可以任意对整行、整列进行异或操作。可以操作任意次。问最后矩阵中最少有多少个1
n<=20,m<=10^5
#Analysis
发现n很小,小到我们可以枚举出行的全部操作状态,且可以将一列看成一个整数。那么只需要快速求出在次状态下列的贡献和。
c s c_s cs为状态为 s s s时的答案,那么如果令 a x a_x ax为初始矩阵中列的值为 x x x的个数, b x b_x bx为值为 x x x的列最少可能有多少个1,有
c k = ∑ i   x o r   k = j a i b j c_k=\sum_{i\ xor\ k=j}a_ib_j ck=i xor k=jaibj
c k = ∑ i   x o r   j = k a i b j c_k=\sum_{i\ xor\ j=k}a_ib_j ck=i xor j=kaibj
于是可以用FWT优化运算。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,b,a) for(int i=b;i>=a;--i)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
char ch;
int read(){int n=0,p=1;for(ch=getchar();ch<'0' || ch>'9';ch=getchar())if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';return n*p;}
const int N=22,S=(1<<22)+2,M=1e5+5;
int n,m,len;
ll c[S],a[S],b[S];
bool map[N][M];
void dwt(ll *a,int sig=1)
{
	for(int i=2;i<=len;i<<=1)
		for(int j=0;j<len;j+=i)
			for(ll *l=a+j,*r=a+j+(i>>1),k=0;k<(i>>1);++l,++r,++k)
			{
				ll u=*l+*r,v=*l-*r;
				*l=u,*r=v;
				if(sig==-1) *l>>=1,*r>>=1;
			}
}
int main()
{
	scanf("%d %d\n",&n,&m);
	fo(i,1,n)
	{
		fo(j,1,m) map[i][j]=getchar()=='1';
		scanf("\n");
	}
	fo(j,1,m)
	{
		int t=0;
		fo(i,1,n) t=t*2+map[i][j];
		++a[t];
	}
	fo(i,0,(1<<n)-1)
	{
		int cnt=0;
		fo(j,0,n-1) if(i&(1<<j)) ++cnt;
		b[i]=min(cnt,n-cnt);
	}
	len=1<<n;
	dwt(a);
	dwt(b);
	fo(i,0,len-1) c[i]=a[i]*b[i];
	dwt(c,-1);
	int ans=n*m;
	fo(i,0,(1<<n)-1) ans=min(ans,c[i]);
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值