CF662C Binary Table fwt

题目描述
给定一个 n m 01 矩阵,可以选择任意行或列进行操作,被选到的行 01 值取反,询问操作后矩阵最少含 1 的数量。
n <= 20 , m <= 10 5

分析:
由于行很少,所以我们考虑对每一列讨论。
f ( i ) 为状态为 i 的列的数目。
g ( i ) 为状态 i 最少 1 的个数,显然 g ( i ) = m i n ( b i t ( i ) , b i t ( r e v ( i ) ) ) b i t ( i ) 表示 i 1 的个数, r e v ( i ) 表示把 i 取反。
h ( i ) 为给行取反的状态,此时,每一个 j 变成了 i   x o r   j ,有

h ( i ) = i   x o r   j = k f ( j ) g ( k )

= j   x o r   k = i f ( j ) g ( k )

直接fwt解决即可。

代码:

#include <iostream>
#include <cmath>
#include <cstdio>
#define LL long long

const int maxn=1048580;

using namespace std;

LL n,m;
LL g[maxn],f[maxn],h[maxn];
bool a[21][100007];
char ch;

LL calc(LL x)
{
    LL sum=0;
    for (LL i=x;i;i-=i&(-i)) sum++;
    return sum;
}

void fwt(LL *a,LL l,LL r)
{
    if (l==r) return;
    LL n=(r-l+1)/2,mid=l+n;
    fwt(a,l,mid-1);
    fwt(a,mid,r);
    for (LL i=l;i<mid;i++)
    {
        LL u=a[i],v=a[i+n];
        a[i]=u+v;
        a[i+n]=u-v;
    }
}

void dwt(LL *a,LL l,LL r)
{
    if (l==r) return;
    LL n=(r-l+1)/2,mid=l+n;
    dwt(a,l,mid-1);
    dwt(a,mid,r);
    for (LL i=l;i<mid;i++)
    {
        LL u=a[i],v=a[i+n];
        a[i]=(u+v)/2;
        a[i+n]=(u-v)/2;
    }
}

int main()
{
    scanf("%lld%lld",&n,&m);        
    for (LL i=1;i<=n;i++)
    {
        scanf("\n");
        for (LL j=1;j<=m;j++)
        {
            scanf("%c",&ch);
            a[i][j]=(ch=='1');
        }
    }       
    for (LL i=0;i<(1<<n);i++)
    {
        LL c=calc(i);
        g[i]=min(c,n-c);
    }   
    for (LL j=1;j<=m;j++)
    {
        LL s=0;
        for (LL i=1;i<=n;i++) s=(s<<1)+a[i][j];
        f[s]++;
    }   
    fwt(f,0,(1<<n)-1);
    fwt(g,0,(1<<n)-1);
    for (LL i=0;i<(1<<n);i++) h[i]=f[i]*g[i];
    dwt(h,0,(1<<n)-1);
    LL ans=0x3f3f3f3f;
    for (LL i=0;i<(1<<n);i++) ans=min(ans,h[i]);
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81738370
FWT