1. 程式人生 > 實用技巧 >[CF662C]Binary Table(FWT)

[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 \bigoplus situ[i]]\)

,下記這個東西為c[mask]。由於有異或,所以我們儘量往異或卷積上湊:

\[{\sum\limits_{i=1}^{m}b[mask \bigoplus situ[i]]} \]

\[=\sum\limits_{x=0}^{2^n-1}(\sum\limits_{i=1}^{m}[situ[i]=x])b[mask \bigoplus x] \]

\[={\sum\limits_{x=0}^{2^n-1}}(\sum\limits_{i=1}^{m}[situ[i]=x])\sum\limits_{y=0}^{2^n-1}[x\bigoplus y=mask]b[y] \]

好了,如果設\(a[x]=\sum_{i=1}^{m}[situ[i]=x]\)

,式子就化簡成了

\[c[mask]=\sum\limits_{x\bigoplus 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;
}