【CF662C】Binary Table
阿新 • • 發佈:2021-01-20
題目
題目連結:https://codeforces.com/problemset/problem/662/C
有一個 \(n\) 行 \(m\) 列的表格,每個元素都是 \(0/1\) ,每次操作可以選擇一行或一列,把 \(0/1\) 翻轉,即把 \(0\) 換為 \(1\) ,把 \(1\) 換為 \(0\) 。請問經過若干次操作後,表格中最少有多少個 \(1\) 。
\(n\leq 20,m\leq 10^5\)。
思路
由於行數很小,所以我們考慮列舉每一行是否翻轉,然後顯然每一列就互相獨立了。假設我們確定了翻轉行的集合 \(S\),答案就是
\[\sum^{m}_{i=1}\min(\mathrm{bit}_{S\ \mathrm{xor}\ a_i},n-\mathrm{bit}_{S\ \mathrm{xor}\ a_i}) \]其中 \(\mathrm{bit}_x\) 表示 \(x\) 二進位制下 \(1\) 的數量。\(a_i\) 表示第 \(i\) 列組成的二進位制數的十進位制表示。
我們預處理出 \(f_i\) 表示數值為 \(i\) 的列的數量,\(g_i\) 表示 \(i\) 二進位制下 \(0\) 的數量和 \(1\) 的數量的較小值。
那麼如果我們列舉翻轉的行的集合為 \(S\),答案為
那麼我們設 \(h_s\) 表示翻轉集合為 \(s\) 時 \(1\) 最少的數量,
\[h_s=\sum_{i\ \mathrm{xor}\ j=s}f_i\times g_j \]FWT 板子。時間複雜度 \(O(n(2^n+m))\)。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1100010,M=22; int n,m,lim,a[M][N]; ll ans,f[N],g[N]; void FWT(ll *f,int typ) { for (int k=1;k<lim;k<<=1) for (int i=0;i<lim;i+=(k<<1)) for (int j=0;j<k;j++) { ll x=f[i+j],y=f[i+j+k]; f[i+j]=(x+y)>>typ; f[i+j+k]=(x-y)>>typ; } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%1d",&a[i][j]); for (int i=1;i<=m;i++) { int s=0; for (int j=1;j<=n;j++) s=s|(a[j][i]<<j-1); f[s]++; } lim=(1<<n); for (int i=1;i<lim;i++) g[i]=g[i-(i&-i)]+1; for (int i=1;i<lim;i++) g[i]=min(g[i],n-g[i]); FWT(f,0); FWT(g,0); for (int i=0;i<lim;i++) f[i]=f[i]*g[i]; FWT(f,1); ans=2e18; for (int i=0;i<lim;i++) ans=min(ans,f[i]); printf("%lld",ans); return 0; }