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

来源:互联网 发布:talking data数据 编辑:程序博客网 时间:2024/06/05 23:57

Description

给定一个N*M01矩阵,可以选择任意行或列,被选择行或列的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);}