[CF662C]Binary Table

luogu

题意

你有一个\(n*m\)\(01\)矩阵。你可以把任意一行或者一列的\(01\)取反。求矩阵中最少的\(1\)的数量。
\(n\le20,m\le10^5\)

sol

很自然地有一个\(O(2^nm)\)的暴力:枚举横行的取反情况,然后纵列就取\(01\)数量较少的一者。
我们记状态\(x\)在原矩阵中的出现次数为\(a[x]\),状态中\(01\)较少一者的数量为\(b[x]\)
会发现当最终的取反状态为\(i\)时,会有\(ans_i=\sum_{j\otimes k=i}a_j*b_k\)
就是一个异或卷积。FWT即可。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define ll long long
const int N = 1<<20;
const int M = 1e5+5;
int n,m,len;ll a[N],b[N];char s[20][M];
void fwt(ll *P,int opt){
    for (int i=1;i<len;i<<=1)
        for (int p=i<<1,j=0;j<len;j+=p)
            for (int k=0;k<i;++k){
                ll x=P[j+k],y=P[j+k+i];
                P[j+k]=(x+y)/opt;P[j+k+i]=(x-y)/opt;
            }
}
int main(){
    n=gi();m=gi();len=1<<n;
    for (int i=0;i<n;++i) scanf("%s",s[i]+1);
    for (int i=1;i<=m;++i){
        int x=0;
        for (int j=0;j<n;++j) x=(x<<1)+s[j][i]-'0';
        ++a[x];
    }
    for (int i=1;i<len;++i) b[i]=b[i>>1]+(i&1);
    for (int i=1;i<len;++i) b[i]=min(b[i],n-b[i]);
    fwt(a,1);fwt(b,1);
    for (int i=0;i<len;++i) a[i]*=b[i];
    fwt(a,2);
    ll ans=a[0];
    for (int i=1;i<len;++i) ans=min(ans,a[i]);
    printf("%I64d\n",ans);return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/9068280.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值