Codeforces 662 C. Binary Table(FWT)

http://codeforces.com/contest/662/problem/C

 

题意:
n行m列01矩阵,每次可以反转一行或一列,问最后最少可以剩下多少个1

 

n只有20,把行状态压缩

操作奇数次相当于1次,操作偶数次相当于不操作

所以可以枚举对行的操作,将操作也状态压缩

A[i] 表示有多少列的状态为i

B[i] 表示 状态为i时的最优解,即fanzh

C[i] 表示 操作i的最优解

执行一次行操作相当于给某一列的状态异或上操作的状态

C[opt] = Σ A[state]*B[opt xor state]

(先执行这种行操作,如果某一列发现再执行一次列操作更优,那这一列再执行列操作,这通通包含在B数组里)

目前求C的复杂度为 2^2n

令 res = opt xor state

C[opt] = Σ(state) Σ(res)   [state xor res == opt] A[state]*B[res]

C[opt] = Σ(state) Σ(res)   [state xor opt == res] A[state]*B[res]

然后就可以用FWT 优化 成2^n * n

 

这还有个用子集反演的,是什么啊??

http://blog.csdn.net/QWsin/article/details/55054071

 

#include<cstdio>
#include<algorithm>

using namespace std;

typedef long long LL;

char s[21][100002];

LL a[1048578],b[1048578],c[1048576];

int count(int i)
{
    int sum=0;
    while(i) sum+=i&1,i>>=1;
    return sum;
}

void FWT_xor(LL *a,int n)
{
    LL x,y;
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;++j)
            {
                x=a[i+j]; y=a[i+j+d];
                a[i+j]=x+y; a[i+j+d]=x-y;
            }
}

void IFWT_xor(LL *a,int n)
{
    LL x,y;
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;++j)
            {
                x=a[i+j]; y=a[i+j+d];
                a[i+j]=x+y>>1; a[i+j+d]=x-y>>1;
            }
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
    int state;
    for(int i=1;i<=m;++i)
    {
        state=0;
        for(int j=1;j<=n;++j)
            if(s[j][i]=='1') state|=1<<j-1;
        ++a[state];
    }
    int S=1<<n,sum;
    for(int i=0;i<S;++i) 
    {
        sum=count(i);
        b[i]=min(sum,n-sum);
    }
    FWT_xor(a,S);
    FWT_xor(b,S);
    for(int i=0;i<S;++i) c[i]=a[i]*b[i];
    IFWT_xor(c,S);
    LL ans=n*m;
    for(int i=0;i<S;++i) ans=min(ans,c[i]);
    printf("%I64d",ans);
}

 

posted @ 2018-03-18 16:26  TRTTG  阅读(421)  评论(0编辑  收藏  举报