codeforces 662C Binary Table FWT

https://codeforces.com/problemset/problem/662/C
在这里插入图片描述
题目大意:给一个nmn*m0101矩阵,每次操作可以选择任意一行或一列翻转,问经过若干次操作后这个矩阵最少还剩下多少11

思路:首先想一下暴力的做法,O(2n)O(2^n)枚举行的翻转情况,那么每一列要么翻转要么不翻转,两种情况取最小值然后累加起来即可,时间复杂度O(2nm)O(2^nm),很明显会超时。首先发现行数很小,那么对于每一列可以用一个二进制数表示该列的状态,设a[i]a[i]表示状态ii出现的次数,设b[i]=ib[i]=i的二进制表示中min(0min(0的数量,11的数量)),那么当枚举的行翻转状态为kk时,答案为:
ans=i=02na[i]b[i xor k]ans=\sum_{i=0}^{2^n}a[i]*b[i\ xor\ k]
F[k]F[k]表示行翻转状态为kk时的答案,上式可以转化为:
F[k]=i xor j=ka[i]b[j]F[k]=\sum_{i\ xor\ j=k} a[i]*b[j]
这不就是FWTFWT的式子嘛。

#include<bits/stdc++.h>
using namespace std;        //FWT
typedef long long ll;

const int maxn=(1<<20)+5;

int n,m,limit;
ll a[maxn],b[maxn],ans[maxn];
char c[20][maxn];

void FWT_xor(ll *A,int inv)
{
    ll x,y;
    for(int mid=1;mid<limit;mid<<=1)
    {
        for(int i=0;i<limit;i+=mid<<1)
        {
            for(int j=0;j<mid;j++)
            {
                x=A[i+j],y=A[mid+i+j];
                A[i+j]=x+y;
                A[mid+i+j]=x-y;
                if(inv==-1)
                {
                    A[i+j]>>=1;
                    A[mid+i+j]>>=1;
                }
            }
        }
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    limit=1<<n;
    for(int i=0;i<n;i++)
        scanf("%s",c[i]);
    for(int i=0;i<m;i++)
    {
        int tmp=0;
        for(int j=0;j<n;j++)
        {
            tmp|=c[j][i]-'0';
            tmp<<=1;
        }
        tmp>>=1;
        a[tmp]++;
    }
    for(int i=0;i<limit;i++)
    {
        int tmp=__builtin_popcount(i);
        b[i]=min(tmp,n-tmp);
    }
    FWT_xor(a,1),FWT_xor(b,1);
    for(int i=0;i<limit;i++)
        ans[i]=a[i]*b[i];
    FWT_xor(ans,-1);
    ll re=ans[0];
    for(int i=1;i<limit;i++)
        re=min(re,ans[i]);
    printf("%lld\n",re);
    return 0;
}


发布了638 篇原创文章 · 获赞 24 · 访问量 3万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐

分享到微信朋友圈

×

扫一扫,手机浏览