Codeforces 662C Binary Table FWT

题意

给出一个n*m的01矩阵,每次操作可以将某一列或某一行取反。问最少剩下多少个1。
n<=20,m<=100000 n <= 20 , m <= 100000

分析

因为n很小,所以可以考虑状压改变哪些行。
设val[T]表示若某一列的状态为T的话对答案的贡献,cnt[T]表示T中1的数量,显然有val[T]=min(cnt[T],n-cnt[T]),s[T]表示有多少列的初始状态为T。
假设改变的状态为S,某一列的状态为T,那么对答案的贡献就是val[S^T]。
设ans[S]表示改变的状态为S时的答案,不难发现有 ans[S]=TxorG=Sval[T]s[G] a n s [ S ] = ∑ T x o r G = S v a l [ T ] ∗ s [ G ]
直接上FWT即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

const int N=1048580;

int n,m,a[100005],cnt[N],bin[25];
char str[100005];
LL s[N],val[N];

void fwt(LL *a,int l,int r)
{
    if (l==r) return;
    int len=(r-l+1)/2,mid=(l+r)/2;
    fwt(a,l,mid);fwt(a,mid+1,r);
    for (int i=0;i<len;i++)
    {
        LL x=a[l+i],y=a[mid+1+i];
        a[l+i]=x+y;a[mid+1+i]=x-y;
    }
}

void dwt(LL *a,int l,int r)
{
    if (l==r) return;
    int len=(r-l+1)/2,mid=(l+r)/2;
    dwt(a,l,mid);dwt(a,mid+1,r);
    for (int i=0;i<len;i++)
    {
        LL x=a[l+i],y=a[mid+1+i];
        a[l+i]=(x+y)/2;a[mid+1+i]=(x-y)/2;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    bin[0]=1;
    for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2;
    for (int i=0;i<bin[n];i++) cnt[i]=cnt[i>>1]+(i&1),val[i]=min(cnt[i],n-cnt[i]);
    for (int i=0;i<n;i++)
    {
        scanf("%s",str+1);
        for (int j=1;j<=m;j++) a[j]+=(str[j]=='1')?bin[i]:0;
    }
    for (int i=1;i<=m;i++) s[a[i]]++;
    fwt(val,0,bin[n]-1);fwt(s,0,bin[n]-1);
    for (int i=0;i<bin[n];i++) s[i]*=val[i];
    dwt(s,0,bin[n]-1);
    int ans=n*m;
    for (int i=0;i<bin[n];i++) ans=min(ans,(int)s[i]);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值