CF662C Binary Table
FWT入门 好题
据说tourist都没有当场做出来
注意到 n n n很小
所以这题一个朴素的暴力就是 2 n 2^n 2n枚举行是否翻转
然后每一列对应一个整数
把这个整数和枚举翻转的数异或一下就是行翻转后的状态
然后考虑列是否翻转
即看0的个数多还是1的个数多
设 f [ i ] f[i] f[i]表示列对应的整数位 i i i的列的个数
设 g [ i ] g[i] g[i]表示 m i n ( c n t 0 , c n t 1 ) min(cnt_0, cnt_1) min(cnt0,cnt1)即0,1出现个数少的那一个
然后可以很容易的发现
a n s [ i ] = ∑ g [ i ⊕ j ] ∗ f [ j ] ans[i] = \sum g[i⊕j] * f[j] ans[i]=∑g[i⊕j]∗f[j]
明显的一个卷积形式
可以变为
a n s [ k ] = ∑ i ⊕ j = k g [ i ] ∗ f [ j ] ans[k] = \sum_{i⊕j = k} g[i] * f[j] ans[k]=∑i⊕j=kg[i]∗f[j]
然后就可以愉快的FWT了
code:
#include<bits/stdc++.h>
#define mod 998244353
#define ll long long
using namespace std;
const int N = (1 << 20) + 5;
void fwt_xor(ll *a, int len, int opt){//板子
for(int i = 2; i <= len; i <<= 1)
for(int p = i >> 1, j = 0; j + i <= len; j += i)
for(int k = j; k < j + p; k ++){
int X = a[k], Y = a[k + p];
a[k] = (X + Y) % mod, a[k + p] = (X - Y + mod) % mod;
if(opt == -1) a[k] = a[k] * 499122177 % mod, a[k + p] = a[k + p] * 499122177 % mod;
}
}
int n, m;
ll a[21][N], b[N], f[N], g[N];
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++){
char c;
scanf(" %c", &c);
a[i][j] = c - '0';//输入
}
for(int i = 0; i < (1 << n); i ++) g[i] = __builtin_popcount(i), g[i] = min(g[i], n - g[i]);//处理g
for(int i = 1; i <= m; i ++){
int t = 0;
for(int j = 1; j <= n; j ++) t = t * 2 + a[j][i];//把这一列转换为一个整数
f[t] ++;//统计个数
}
int len = (1 << n);
fwt_xor(f, len, 1), fwt_xor(g, len, 1);//FWT
for(int i = 0; i < len; i ++) f[i] = f[i] * g[i] % mod;
fwt_xor(f, len, -1);
ll ans = n * m + 1;
for(int i = 0; i < len; i ++) ans = min(ans, f[i]);
printf("%lld", ans);
return 0;
}