CF662C Binary Table【FWT】
阿新 • • 發佈:2020-07-18
題意:
給出一個\(n\times m\)的\(01\)矩陣,每次可以反轉一行或者一列,問經過若干次反轉之後,最少有多少個\(1\)
\(n\le 20, m\le 10^5\)
題解:
可以把每一列看作一個二進位制數,這樣得到\(m\)個二進位制數,記為\(A\),翻轉第\(i\)列就相當於把每個二進位制數異或上\(1<<i\),由於\(n\)很小,所以列舉所有的翻轉組合,一共\(2^n\)種,令\(d(x)\)表示最高位為\(n\)的二進位制數中\(0\)和\(1\)數量的最大值,那麼答案可以表示為:
\[\sum_{msk=0}^{2^n-1}(\sum_{i=1}^m d(msk\oplus A[i])) \]
轉化一下得到:
\[\sum_{msk=0}^{2^n-1}(\sum_{x\oplus y=msk} d(x) \times c(y)) \]
其中\(c(y)\)表示\(y\)出現的次數
接下來跑\(FWT\)就好了
view code
//#pragma GCC optimize("O3") //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace std; function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);}; const int MAXN = 2e6+7; const int MOD = 998244353; const int inv2 = (MOD + 1) / 2; int n, m, A[MAXN], f[MAXN], N, c[MAXN]; void FWT_xor(int *a,int opt){ for(int i=1;i<N;i<<=1) for(int p=i<<1,j=0;j<N;j+=p) for(int k=0;k<i;++k){ int X=a[j+k],Y=a[i+j+k]; a[j+k]=(X+Y)%MOD;a[i+j+k]=(X+MOD-Y)%MOD; if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%MOD,a[i+j+k]=1ll*a[i+j+k]*inv2%MOD; } } char buf[MAXN]; int main(){ scanf("%d %d",&n,&m); for(int i = 0; i < n; i++){ scanf("%s",buf+1); for(int j = 1; j <= m; j++) A[j] ^= ((buf[j]-'0')<<i); } N = 1 << n; for(int i = 0; i < N; i++){ f[i] = __builtin_popcount(i); f[i] = min(f[i],n-f[i]); } for(int i = 1; i <= m; i++) c[A[i]]++; FWT_xor(f,1); FWT_xor(c,1); for(int i = 0; i < N; i++) f[i] = 1ll * f[i] * c[i] % MOD; FWT_xor(f,-1); cout << *min_element(f,f+N) << endl; return 0; }