• Binary Table CodeForces


    大意: 给定$nm$大小的$01$矩阵, $1le nle 20,1le mle 1e5$, 可以任选行列翻转, 求最终$1$总数最少为多少.

    显然有$O(m2^n)$的暴力算法

    也就是枚举翻转哪些行, 然后对于一列, 若$1$的个数多于$0$的个数就翻转.

    可以发现对于相同的列, 翻转行对它的影响是相同的.

    用$a_i$记录状态为$i$的列的个数, $b_i$记录状态为$i$的列的贡献.

    假设翻转行状态为$S$时答案为$f_{S}$, 枚举每种状态的列的贡献, 就有

    $$f_{S}=sumlimits_{i} a_{i oplus S}b_{i}$$

    明显的$xor$卷积形式, 可以用$FWT$求出.

    #include <iostream>
    #include <cstdio>
    #define REP(i,a,n) for(int i=a;i<=n;++i)
    using namespace std;
    typedef long long ll;
    
    const int N = 1e5+10, M = (1<<20)+10;
    int n, m;
    ll a[M], b[M];
    char s[22][N];
    
    void FWT(ll *a, int n, int tp) {
        for (int i=0; (1<<i)<n; ++i) {
            REP(j,0,n-1) if (j>>i&1) {
                ll l = a[j^1<<i], r = a[j];
                a[j^1<<i] += r;
                a[j] = l-r;
            }
        }
        if (tp==-1) REP(i,0,n-1) a[i]/=n;
    }
    
    void mul(ll *a, ll *b, int n) {
    	FWT(a,n,1),FWT(b,n,1);
    	REP(i,0,n-1) a[i]*=b[i];
    	FWT(a,n,-1);
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	REP(i,1,n) scanf("%s",s[i]+1);
    	REP(i,1,m) {
    		int x = 0;
    		REP(j,1,n) (x<<=1)|=s[j][i]=='1';
    		++a[x];
    	}
    	REP(i,0,(1<<n)-1) { 
    		int t = __builtin_popcount(i);
    		b[i] = min(t, n-t);
    	}
    	mul(a,b,1<<n);
    	ll ans = a[0];
    	REP(i,1,(1<<n)-1) ans = min(ans, a[i]);
    	printf("%lld
    ", ans);
    }
    
  • 相关阅读:
    Nexus入门指南(图文)[转]
    java注解[转]
    JS设置IE可信站点及ActiveX设置
    ExtJS 4 树
    SQL大全
    基于Spring aop 和JAVA注解方式添加日志
    Excle自动增长序号
    VS 生成后事件
    Oracle命令分解之正则表达式搜索(一)
    Oracle命令分解之……SOUNDEX
  • 原文地址:https://www.cnblogs.com/uid001/p/11217048.html
Copyright © 2020-2023  润新知