CF662C Binary Table【FWT】

CF662C Binary Table

题意:

给出一个(n imes m)(01)矩阵,每次可以反转一行或者一列,问经过若干次反转之后,最少有多少个(1)
(nle 20, mle 10^5)

题解:

可以把每一列看作一个二进制数,这样得到(m)个二进制数,记为(A),翻转第(i)列就相当于把每个二进制数异或上(1<<i),由于(n)很小,所以枚举所有的翻转组合,一共(2^n)种,令(d(x))表示最高位为(n)的二进制数中(0)(1)数量的最大值,那么答案可以表示为:

[sum_{msk=0}^{2^n-1}(sum_{i=1}^m d(mskoplus A[i])) ]

转化一下得到:

[sum_{msk=0}^{2^n-1}(sum_{xoplus y=msk} d(x) imes c(y)) ]

其中(c(y))表示(y)出现的次数

接下来跑(FWT)就好了

view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e6+7;
const int MOD = 998244353;
const int inv2 = (MOD + 1) / 2;
int n, m, A[MAXN], f[MAXN], N, c[MAXN];

void FWT_xor(int *a,int opt){
    for(int i=1;i<N;i<<=1)
        for(int p=i<<1,j=0;j<N;j+=p)
            for(int k=0;k<i;++k){
                int X=a[j+k],Y=a[i+j+k];
                a[j+k]=(X+Y)%MOD;a[i+j+k]=(X+MOD-Y)%MOD;
                if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%MOD,a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
            }
}
char buf[MAXN];
int main(){
    scanf("%d %d",&n,&m);
    for(int i = 0; i < n; i++){
        scanf("%s",buf+1);
        for(int j = 1; j <= m; j++) A[j] ^= ((buf[j]-'0')<<i);
    }
    N = 1 << n;
    for(int i = 0; i < N; i++){
        f[i] = __builtin_popcount(i);
        f[i] = min(f[i],n-f[i]);
    }
    for(int i = 1; i <= m; i++) c[A[i]]++;
    FWT_xor(f,1); FWT_xor(c,1);
    for(int i = 0; i < N; i++) f[i] = 1ll * f[i] * c[i] % MOD;
    FWT_xor(f,-1);
    cout << *min_element(f,f+N) << endl; 
    return 0;
}