題目連接c++
不妨規定先翻轉某些行,再翻轉某些列。ui
設\(F[x]=\min(\mathbb{pop}(x),\mathbb{pop}((2^n-1)\veebar x))\),\(S_i\)爲第\(i\)列的表格狀態。spa
枚舉每一行的翻轉狀況\(p\),此時的答案爲\(\sum_i F[S_i\veebar p]\)。code
總體答案
\[ 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] \]element
記\(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] \]get
所以直接對\(F,T\)作XOR卷積,拿出結果的最小值便可。it
#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; }