设 \(a[i]\) 为二进制为 \(i\) 的列的数量, \(b[i]\) 为 \(i\) 二进制下前 \(n\) 位中 \(0/1\) 出现的较小值。
假设我们对列的修改状态为 \(i\) ,那么这时的 \(ans\) 为 \(\displaystyle \sum_{j}a[j] \times b[i \bigoplus j]\)
设 \(i \bigoplus j =k\) ,那么就变成了 \(\displaystyle \sum_{j \bigoplus k = i}a[j] \times b[k]\)
就变成了异或卷积的形式,用 \(FWT\) 处理,最后取个 \(min\) 即可。
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
int n, m, lim, ans = 1e9;
const int N = 2000005;
int a[N], b[N], g[N], cnt[N];
char s[N];
void FWT(int *A, int lim, int opt)
{
int a, b;
for (int len = 2, mid = 1; mid < lim; len <<= 1, mid <<= 1)
for (int j = 0; j < lim; j += len)
for (int k = j; k < j + mid; ++k)
{
a = A[k]; b = A[k + mid];
if (opt == 1)A[k] = a + b, A[k + mid] = a - b;
else A[k] = (a + b) >> 1, A[k + mid] = (a - b) >> 1;
}
}
signed main()
{
cin >> n >> m; lim = 1 << n;
for (int i = 1; i <= n; ++i)
{
scanf("%s", s + 1);
for (int j = 1; j <= m; ++j)
if (s[j] == '1')g[j] |= (1 << (i - 1));
}
for (int i = 1; i <= m; ++i)++a[g[i]];
for (int i = 1; i < lim; ++i)cnt[i] = cnt[i >> 1] + (i & 1);
for (int i = 0; i < lim; ++i)b[i] = min(cnt[i], n - cnt[i]);
FWT(a, lim, 1); FWT(b, lim, 1);
for (int i = 0; i < lim; ++i)a[i] = a[i] * b[i];
FWT(a, lim, 2);
for (int i = 0; i < lim; ++i)ans = min(ans, a[i]);
cout << ans;
return 0;
}