[CF662C]Binary Table


题解

刚刚学习了\(FWT\),然后看到这道题还是想不出来

首先发现\(n\)很小,所以枚举每一行是否翻转是可行的
然后这样以后对于每一列的状态都要异或上行翻转的状态
这样每一列不会对其他列造成影响
就看翻转与不翻转谁优
这样我们就有了\(O(2^nm)\)的做法
这样复杂度太高了
我们发现一列的状态相同的可以合在一起
\(a[i]\)表示一列的状态为\(i\)\(0,1\)个数的较小值
\(b[i]\)表示一列状态为\(i\)的有多少个
\(f[i]\)表示行翻转状态为\(i\)时的答案
那么\(f[S]=\sum_{j=i \oplus S}{b[i] * a[j]}\)
但是异或有优秀的对称差的性质啊
\(i \oplus j=k -> i \oplus k=j\)
所以\(f[S]=\sum_{S=i \oplus j}{b[i]*a[j]}\)

题解

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 4500005 ;
const int N = 100005 ;
const int INF = 1e9 ;
using namespace std ;

int n , m , lim ;
char s[21][N] ;
LL a[M] , b[M] , ans = INF ;
inline int Calc(int S) {
	int ret = 0 ;
	for(int i = 0 ; i < n ; i ++)
		if(S & (1 << i))
			++ ret ;
	return min(ret , n - ret) ;
}
inline void FWT_xor(LL *A , int unit) {
	for(int mid = 1 ; mid < lim ; (mid <<= 1)) {
		int R = (mid << 1) ;
		for(int j = 0 ; j < lim ; j += R) 
			for(int k = 0 ; k < mid ; k ++) {
				LL x = A[j + k] , y = A[j + k + mid] ;
				A[j + k] = x + y ; ; A[j + k + mid] = x - y ;
				if(unit < 0) A[j + k] /= 2 , A[j + k + mid] /= 2 ;
			}
	}
}
int main() {
	scanf("%d%d",&n,&m) ; 
	for(int i = 1 ; i <= n ; i ++) scanf("%s",s[i] + 1) ;
	for(int i = 0 ; i < (1 << n) ; i ++) 
		a[i] = Calc(i) ;
	for(int i = 1 , S ; i <= m ; i ++) {
		S = 0 ;
		for(int j = 1 ; j <= n ; j ++) 
			S = (S << 1) + (s[j][i] - '0') ;
		++ b[S] ;
	}
	lim = 1 ; while(lim < (1 << n)) lim <<= 1 ;
	FWT_xor(a , 1) ; FWT_xor(b , 1) ;
	for(int i = 0 ; i < lim ; i ++) b[i] = a[i] * b[i] ;
	FWT_xor(b , -1) ;
	for(int i = 0 ; i < (1 << n) ; i ++)
		ans = min(ans , b[i]) ;
	printf("%lld\n",ans) ;
	return 0 ;
}
posted @ 2019-03-15 07:02  beretty  阅读(325)  评论(0编辑  收藏  举报