Codeforces 662C Binary Table 快速沃尔什变换(FWT)

Description

给定一个 N *M 01 矩阵,可以选择任意行或列,被选择行或列的 01 值取反,问经过操作矩阵最少的含 1 的数量。

Data Constraint

N<= 20 , M <=105

Solution

考虑把每一列压成 20 位的二进制数,对行的操作也可以看成一个 20 位的整数,同时显然有,同一行或同一列最多操作一次,操作的先后顺序对矩阵的最终状态不会有影响。
ai 表示第 i 列上的二进制数,考虑对行的操作为x(每一列异或上 x )时,整个矩阵的含1的最少数量,对于每一个 ai ,设 bi = ai xor x ,对每一个bi判断取反后是否更优,统计总和后取最小即可。

上述做法的时间复杂度为 O (2N* M ),现在考虑优化,设Ci表示i这个数取反和不取反两种情况含 1 的最小个数,设Ex表示表示列的二进制数等于 x 的个数,Fx表示对行的操作为 x 时(每一列异或上x)矩阵含 1 个最小个数,显然有

Fx=ixorx=kCkEi

等价于

Fx=ixork=xCkEi

上述卷积形式用 FWT 实现即可。时间复杂度 O (N* 2N )。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)
#define min(a,b) ((a)<(b)?(a):b)
using namespace std;
typedef long long ll;
const ll N=4e6;
int a[N],n,m,j,k,l,i,o,p;
ll aa[N],bb[N],cc[N];

int qz(int o)
{
    int yy=0;
    for(;o;o>>=1)yy+=(o&1);
    return min(n-yy,yy);
}

void DWT(long long *a,int n)
{
    ll k=0;
    for(int m=2;m<=n;m<<=1)
    {
        int half=m/2;
        for(i=0;i<n-1;i+=m)
        fo(l,i,i+half-1){
            k=a[l+half];
            a[l+half]=a[l]-k;
            a[l]=a[l]+k;
        }
    }
}

void UNDWT(long long *a,int n)
{
    ll k=0;
    for(int m=2;m<=n;m<<=1){
        int half=m/2;
        for(int i=0;i<n-1;i+=m)
        fo(l,i,i+half-1){
            k=a[l+half];
            a[l+half]=(a[l]-k)/2;
            a[l]=(a[l]+k)/2;
        }
    }
}

void FWT(long long *a,long long *b,long long *c,int len)
{
    DWT(a,len); DWT(b,len);
    fo(i,0,len-1)c[i]=a[i]*b[i];
    UNDWT(c,len);
}

int main()
{
    cin>>n>>m;
    char k=getchar();
    fo(i,1,n){
        fo(l,1,m)a[l]=(a[l]<<1)+(getchar()-48);
        char k=getchar();
    }
    fo(i,1,m)++aa[a[i]];
    p=1<<n;
    fo(i,0,p-1)bb[i]=qz(i);
    FWT(aa,bb,cc,p);
    ll ans=(ll)m*(ll)m*(ll)n; 
    fo(i,0,p-1)
    ans=min(ans,cc[i]);
    printf("%I64d",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值