CF662 C. Binary Table

题目传送门:CF

题目大意:

给定一个\(n\times m\)的表格\((n\leqslant 20,m\leqslant 10^5)\)

每个表格中有\(0/1\),每次可以将一行或者一列翻转,问表格中最少有多少个1


首先\(n\)很小,状压是肯定躲不掉了……

然后我们发现,只要确定了翻转一些行,那么答案必然唯一确定(每列取\(\min\{Num_0,Num_1\}\)

于是我们设\(f[i]\)表示翻转行状态为\(i\)的答案

统计答案的时候,相同状态的列是可以合并的,所以我们设\(C[i]\)表示列状态为\(i\)的列个数

然后我们假定当前列状态为\(i\),翻转的行状态为\(S\),那么翻转后状态就为\(i\oplus S\)

然后对于每一列的任意状态\(i\),我们都知道其答案,为\(\min\{Num_0,Num_1\}\),预处理为\(g[i]\)

那么答案即为\(f[k]=\sum\limits_{i\oplus j=k}C[i]\times g[j]\)

FWT优化xor卷积即可

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
#define lowbit(x) ((x)&-(x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
    int x=0,f=1; char ch=gc();
    for (;ch<'0'||ch>'9';ch=gc())   if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline int read(){
    int x=0,f=1; char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x<0)    putchar('-'),x=-x;
    if (x>9)    print(x/10);
    putchar(x%10+'0');
}
const int N=1e5;
void FWT(ll *a,int n,int type){
    for (int i=2;i<=n;i<<=1){
        for (int j=0;j<n;j+=i){
            for (int k=0;k<i>>1;k++){
                ll x=a[j+k],y=a[j+k+(i>>1)];
                a[j+k]=x+y,a[j+k+(i>>1)]=x-y;
                if (!~type) a[j+k]>>=1,a[j+k+(i>>1)]>>=1;
            }
        }
    }
}
ll f[(1<<20)+10],A[(1<<20)+10],B[(1<<20)+10];
char s[25][N+10];
int main(){
    int n=read(),m=read();ll Ans=inf;
    for (int i=0;i<n;i++)   scanf("%s",s[i]);
    for (int j=0;j<m;j++){
        int sta=0;
        for (int i=0;i<n;i++)   sta=(sta<<1)+s[i][j]-'0';
        A[sta]++;
    }
    for (int i=1;i<1<<n;i++)    B[i]=B[i-lowbit(i)]+1;
    for (int i=0;i<1<<n;i++)    B[i]=min(B[i],n-B[i]);
    FWT(A,1<<n,1),FWT(B,1<<n,1);
    for (int i=0;i<1<<n;i++)    f[i]=A[i]*B[i];
    FWT(f,1<<n,-1);
    for (int i=0;i<1<<n;i++)    Ans=min(Ans,f[i]);
    printf("%lld\n",Ans);
    return 0;
}

转载于:https://www.cnblogs.com/Wolfycz/p/10436637.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值