\(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;
}