CF662C Binary Table

FWT板子w

就是一个显然的做法就是枚举哪些行翻转,然后对于每一列贪心取翻转或者不翻即min(count(i),n-count(i))

这样肯定是过不去的 我们来考虑优化

我们记录数组F表示对于一个数i它的较优翻转 即上面的那个柿子

然后再记录一个数组表示原来的矩阵中每一列的计数 cnt[i]表示将一列看成一个二进制数 这个数是i的列数

对于答案我们有ans[k] = \sum_{i\otimes j=k}f[i]*cnt[j]就是考虑计算贡献 ans[k]表示行的翻转是k

由于k \otimes j = i是等价于i \otimes j=k的所以我们可以转化成卷积形式然后套上异或FWT即可

【码风日益毒瘤】代码。

//Love and Freedom.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mxn (1<<20)
#define int ll
using namespace std;

int f[mxn+10],cnt[mxn+10];
int ans[mxn+10];

void fwt(int *a,int n,int f)
{
	for(int k=2,mid=1;k<=n;k<<=1,mid<<=1)
	{
		for(int i=0;i<n;i+=k)	for(int j=0;j<mid;j++)
		{
			int x=a[i+j],y=a[i+mid+j];
			if(f>0)	a[i+j] = x+y,a[i+mid+j] = x-y;
			else	a[i+j] = (x+y)/2,a[i+mid+j] = (x-y)/2;
		}
	}
}
char ch[100010];
int mp[21][100010];
int count(int x)
{
	int ct=0;
	while(x)	x=x&(x-1),ct++;
	return ct;
}
signed main()
{
	int n,m;
	scanf("%lld%lld",&n,&m);
	for(int i=0;i<n;i++)
	{
		scanf("%s",ch);
		for(int j=0;j<m;j++)
			mp[i][j]=ch[j]-'0';
	}
	for(int i=0;i<m;i++)
	{
		int tmp=0;
		for(int j=0;j<n;j++)
			tmp|=mp[j][i],tmp<<=1;
		tmp>>=1; cnt[tmp]++;
	}
	int qwq=0,top=1<<n;
	for(int i=0;i<top;i++)	qwq=count(i),f[i]=min(qwq,n-qwq);
	fwt(f,top,1); fwt(cnt,top,1);
	for(int i=0;i<top;i++)	ans[i] = f[i] * cnt[i];
	fwt(ans,top,-1);
	int fin=n*m;
	for(int i=0;i<top;i++)	fin=min(fin,ans[i]);
	printf("%lld\n",fin);
	return 0;
}

 

posted @ 2019-01-07 11:02  寒雨微凝  阅读(103)  评论(0编辑  收藏  举报