CF662C Binary Table(FWT)

[Luogu-CF662C]

FWT_xor

题目描述

有一个 \(n\)\(m\) 列的表格,每个元素都是 $0/1 $,每次操作可以选择一行或一列,把 \(0/1\) 翻转,即把 \(0\) 换为 \(1\) ,把 \(1\) 换为 \(0\) 。请问经过若干次操作后,表格中最少有多少个 \(1\)

首先可以想到\(O(2^N*M)\)的暴力,即枚举每一行是否翻转,然后\(O(M)\)计算

\(f_k\)表示选择了状态为\(k\)的那些行,\(a_i\)表示有多少列的二进制表示等于\(i\)\(b_j\)表示\(j\)\(0\)个数和\(1\)个数的较小值,

我们有\(f_k=∑_{i⊗k=j}{a_i}{b_j}\),这相当于枚举对哪些行进行操作(\(k\)),然后对于二进制表示为\(i\)的列(有\(a_i\)列,做完行变换后这一列的值为\(i⊗k\)),贪心(\(b_j\))地选择是否对这一列做变换,用异或的性质把\(j,k\)互换,我们得到\(f_k=∑_{i⊗j=k}{a_i}{b_j}\),这是一个卷积的形式,可以用FWT快速求得答案

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

const int N=20;
const int MAXN=1<<N;
const int MAXM=1e5+5;

LL a[MAXN],b[MAXN];
char s[N][MAXM];
int n,m,len;

inline void FWT(LL *A,int type){
    for(int i=1;i<len;i<<=1)
        for(int j=0;j<len;j+=(i<<1))
            for(int k=0;k<i;k++){
                LL x=A[j+k],y=A[j+i+k];
                A[j+k]=x+y,A[j+i+k]=x-y;
                if(type==-1) A[j+k]/=2,A[j+i+k]/=2;
            }
}

int main(){
    n=read(),m=read();len=1<<n;
    for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    for(int i=1;i<=m;i++){
        int x=0;
        for(int j=1;j<=n;j++) x=(x<<1)+s[j][i]-'0';
        a[x]++;
    }
    for(int i=0;i<len;i++) b[i]=b[i>>1]+(i&1);
    for(int i=0;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,-1);
    LL ans=a[0];
    for(int i=1;i<len;i++) ans=min(ans,a[i]);
    printf("%lld\n",ans);
}

转载于:https://www.cnblogs.com/lizehon/p/10546144.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值