https://atcoder.jp/contests/abc396/tasks/abc396_g
下面给出题目的中文思路、详细解释以及带中文注释的 C++ 实现代码。
代码实现
下面给出带中文注释的 C++ 代码:
#include <bits/stdc++.h>
using namespace std;typedef long long ll;// 快速Walsh-Hadamard变换(FWHT)
// 参数 a 为待变换数组,参数 invert 为 false 时求正变换,为 true 时求反变换(需要除以数组大小)。
void fwht(vector<ll>& a, bool invert) {int n = a.size();for (int len = 1; len < n; len <<= 1) {for (int i = 0; i < n; i += (len << 1)) {for (int j = 0; j < len; j++) {ll u = a[i+j], v = a[i+j+len];a[i+j] = u + v;a[i+j+len] = u - v;}}}if (invert) {for (int i = 0; i < n; i++){a[i] /= n;}}
}int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int H, W;cin >> H >> W;int size = 1 << W; // 一共有 2^W 种可能的行模式// freq[mask] 记录每种行模式出现的次数vector<ll> freq(size, 0);for (int i = 0; i < H; i++){string s;cin >> s;int mask = 0;// 将字符串 s 转换为整数 mask(二进制表示)for (int j = 0; j < W; j++){mask = (mask << 1) | (s[j]-'0');}freq[mask]++;}// 构造数组 G,其中 G[x] = min(popcount(x), W - popcount(x))// 对于某一行模式 R,固定列翻转方案 C 后,// 若不翻转该行,贡献为 popcount(R XOR C);// 若翻转该行,贡献为 W - popcount(R XOR C);// 取两者中的较小值,即为 min(popcount(R XOR C), W-popcount(R XOR C))。// 将 R XOR C 换个变量记作 x,则我们令 G(x)=min(popcount(x), W-popcount(x))vector<ll> G(size, 0);for (int mask = 0; mask < size; mask++){int ones = __builtin_popcount(mask);G[mask] = min(ones, W - ones);}// 我们要求的 f(C) = sum_{R} freq[R] * G(R XOR C)// 这正是 freq 与 G 的 XOR 卷积 (freq ⊕ G)[C] 的定义:// (freq ⊕ G)[C] = sum_{R} freq[R] * G(C XOR R)// 因此,我们可以利用 FWHT 来求解所有 f(C)。// 对 freq 和 G 分别做 FWHTvector<ll> F = freq; // F 保存 freq 的变换结果vector<ll> Hvec = G; // Hvec 保存 G 的变换结果fwht(F, false);fwht(Hvec, false);// 逐点相乘vector<ll> conv(size, 0);for (int i = 0; i < size; i++){conv[i] = F[i] * Hvec[i];}// 对乘积结果做反变换,即得到 XOR 卷积结果fwht(conv, true);// conv[C] 即为 f(C) = sum_{R} freq[R] * G(R XOR C)// 答案为所有可能列翻转方案 C 中 f(C) 的最小值ll ans = LLONG_MAX;for (int i = 0; i < size; i++){ans = min(ans, conv[i]);}cout << ans << "\n";return 0;
}