[CF662C]Binary Table(FWT)

题面

http://codeforces.com/contest/662/problem/C

题解

前置知识

看到n仅仅20,自然想到状压。

因为每一行、列最多操作一次即可,所以注意到一个性质:如果每一行分别有没有操作已经确定,答案就确定了;因为每一列是否操作是互相独立的,如果操作能使答案变小就操作,否则就不操作。

形式化地说,设num[x]表示x的二进制表示中1的个数,并定义

[b[x] = min (num[x],num[2^n-x-1]) ]

然后,每一行是否操作状压成一个数mask,初始时每一列的状态压成situ[1]~situ[m],那么对于固定的mask,答案是(sum_{i=1}^{m}b[mask igoplus situ[i]]),下记这个东西为c[mask]。由于有异或,所以我们尽量往异或卷积上凑:

[{sumlimits_{i=1}^{m}b[mask igoplus situ[i]]} ]

[=sumlimits_{x=0}^{2^n-1}(sumlimits_{i=1}^{m}[situ[i]=x])b[mask igoplus x] ]

[={sumlimits_{x=0}^{2^n-1}}(sumlimits_{i=1}^{m}[situ[i]=x])sumlimits_{y=0}^{2^n-1}[xigoplus y=mask]b[y] ]

好了,如果设(a[x]=sum_{i=1}^{m}[situ[i]=x]),式子就化简成了

[c[mask]=sumlimits_{xigoplus y=mask}a[x]b[y] ]

FWT结束此题。

总时间复杂度(O(2^nn))

代码

#include<bits/stdc++.h>

using namespace std;

#define ll long long
#define rg register
#define In inline

const ll N = 1048576;
const ll M = 100000;

In ll read(){
	ll s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

In void calc(ll &x,ll &y,ll opt){
	if(opt == 1){
		ll X = x + y,Y = x - y;
		x = X,y = Y;
	}
	else{
		ll X = (x + y) >> 1,Y = (x - y) >> 1;
		x = X,y = Y;
	}
}

void FWT(ll a[],ll deg,ll opt){
	for(rg int n = 2;n <= deg;n <<= 1){
		int m = n >> 1;
		for(rg int i = 0;i < deg;i += n)
			for(rg int j = 0;j < m;j++)
				calc(a[i+j],a[i+j+m],opt);
	}
}

ll n,m,deg;
ll a[N+5],b[N+5],c[N+5],num[N+5];
ll situ[M+5];
char s[M+5];

int main(){
	n = read();m = read(),deg = 1ll << n;
	for(rg int i = 1;i <= n;i++){
		scanf("%s",s + 1);
		for(rg int j = 1;j <= m;j++)situ[j] = ((situ[j]<<1) | (s[j]-'0'));
	}
	for(rg int i = 1;i <= m;i++)a[situ[i]]++;
	for(rg int i = 1;i < deg;i++)num[i] = num[i>>1] + (i&1);
	for(rg int i = 0;i < deg;i++)b[i] = min(num[i],num[deg-i-1]);
	FWT(a,deg,1);
	FWT(b,deg,1);
	for(rg int i = 0;i < deg;i++)c[i] = a[i] * b[i];
	FWT(c,deg,-1);
	ll ans = n * m;
	for(rg int i = 0;i < deg;i++)ans = min(ans,c[i]);
	cout << ans << endl;
	return 0;
}
原文地址:https://www.cnblogs.com/xh092113/p/12380895.html