zoukankan      html  css  js  c++  java
  • Codeforces #662C Binary Table

    听说这是一道$ Tourist$现场没出的题

    Codeforces #662C

    题意:

    给定$n*m的 01$矩阵,可以任意反转一行/列($0$变$1$,$1$变$0$),求最少$ 1$的数量

    $ n<=20 m<=100000$


    $ Solution$

    考虑暴力

    枚举每一行反转/不反转

    预处理$ g(s)$表示某状态为$ s$的列的最少$ 1$的数量

    显然$ g(s)=min(popcount(s),n-popcount(s))$

    枚举每行是否反转之后直接$ O(m)$计算即可

    时间复杂度$ O(2^n m)$,无法通过这题

    容易发现瓶颈在于暴力枚举行状态之后无法快速计算答案

    我们令$ f(s)$表示列状态为$ s$的列的出现次数,$ F(s)$表示行反转状态为$ s$的时候的答案

    转移有$ F(s)=sumlimits_{i=0}^{2^n-1}f(i)g(i xor s)$

    由于$ i xor i   xor   s = s$

    所以可以化简为$ F(s)=sumlimits_{i xor j =s}f(i)g(j)$

    是一个$ FWT$卷积的形式

    直接$ FWT$优化

    时间复杂度:$ O(nm+2^n n)$

    注意$ FWT$过程中可能要开$ long long$


    $ my code:$

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #define rt register int
    #define ll long long
    using namespace std;
    inline ll read(){
        ll x = 0; char zf = 1; char ch = getchar();
        while (ch != '-' && !isdigit(ch)) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    int i,j,k,m,n,x,y,z,cnt,invn;
    void fwt(int n,ll *a,int fla){
        for(rt i=1;i<n;i<<=1)
        for(rt j=0;j<n;j+=i<<1)
        for(rt k=0;k<i;k++){
            ll x=a[j+k],y=a[i+j+k];
            a[j+k]=x+y;a[i+j+k]=x-y;
        }
        if(fla==-1)for(rt i=0;i<n;i++)a[i]/=n;
    }
    char c[22][100010];
    int s[100010];ll f[1048578],g[1048578];
    #define cnt(x) __builtin_popcount(x)
    int main(){
        n=read();m=read();
        for(rt i=1;i<=n;i++)scanf("%s",c[i]+1);
        for(rt i=1;i<=n;i++)
        for(rt j=1;j<=m;j++)s[j]=s[j]<<1|(c[i][j]=='1');
        for(rt i=1;i<=m;i++)g[s[i]]++;
        for(rt i=0;i<(1<<n);i++)f[i]=min(cnt(i),n-cnt(i));
        fwt(1<<n,f,1);fwt(1<<n,g,1);
        for(rt i=0;i<1<<n;i++)f[i]=f[i]*g[i];
        fwt(1<<n,f,-1);
        cout<<*min_element(f,f+(1<<n));
        return 0;
    }
  • 相关阅读:
    JAVA传值与传址
    JAVA中的栈和堆
    for语句输出图形
    Linux下ps命令详解(转载)
    BMC介绍
    JVM 优化、内存泄露排查、gc.log 分析方法等(转载)
    动态从zookeeper读取kafka信息
    centos 查看文件系统类型
    TCPdump抓包命令详解
    Linux Tab键自动补齐
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10038268.html
Copyright © 2011-2022 走看看