[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 ;
}