[CF662C] Binary Table

题目链接

不妨规定先翻转某些行,再翻转某些列。

\(F[x]=\min(\mathbb{pop}(x),\mathbb{pop}((2^n-1)\veebar x))\)\(S_i\)为第\(i\)列的表格状态。

枚举每一行的翻转情况\(p\),此时的答案为\(\sum_i F[S_i\veebar p]\)

整体答案

\[ans =\min_{p=0}^{2^n-1} \sum_{i=0}^{m-1} F[S_i\veebar p] =\min_{p=0}^{2^n-1} \sum_{q=0}^{2^n-1}F[q]\sum_{i=0}^{m-1} [S_i\veebar p=q] \]

\(T[x]=\sum_{i=0}^{m-1}[S_i=x]\),则

\[ans =\min_{p=0}^{2^n-1} \sum_{q=0}^{2^n-1}F[q]T[p\veebar q] =\min_{p=0}^{2^n-1} \sum_{x=0}^{2^n-1}\sum_{y=0}^{2^n-1}[x\veebar y=p]F[x]T[y] \]

因此直接对\(F,T\)做XOR卷积,拿出结果的最小值即可。

我受够了你谷公式渲染, 求您们修修MathJax吧

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int L=1<<20;
const int N=1e5;

int n,m,s[N];
ll F[L],T[L]; 

void FWT_XOR(ll a[],int len) {
	for(int m=1; m<len; m<<=1)
	for(int i=0,s=m<<1; i<len; i+=s)
	for(int j=0; j<m; ++j) {
		ll x=a[i+j], y=a[i+m+j];
		a[i+j]=x+y, a[i+m+j]=x-y;
	}
}
void IFWT_XOR(ll a[],int len) {
	for(int m=1; m<len; m<<=1)
	for(int i=0,s=m<<1; i<len; i+=s)
	for(int j=0; j<m; ++j) {
		ll x=a[i+j], y=a[i+m+j];
		a[i+j]=(x+y)/2;
		a[i+m+j]=(x-y)/2;
	}
}

int main() {
	scanf("%d%d",&n,&m);
	int len=1<<n,col;
	for(int i=0; i<n; ++i)
	for(int j=0; j<m; ++j) 
		scanf("%1d",&col),s[j]|=col<<i;
	for(int i=0; i<len; ++i) 
		F[i]=min(__builtin_popcount(i),__builtin_popcount((len-1)^i));
	for(int i=0; i<m; ++i) T[s[i]]++;
	FWT_XOR(F,len);
	FWT_XOR(T,len);
	for(int i=0; i<len; ++i) F[i]*=T[i];
	IFWT_XOR(F,len);
	printf("%lld\n",*min_element(F,F+len));
	return 0;
}

posted @ 2019-07-05 17:14  nosta  阅读(287)  评论(0编辑  收藏  举报