• [CF662C]Binary Table(FWT)


    题面

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

    题解

    前置知识

    看到n仅仅20,自然想到状压。

    因为每一行、列最多操作一次即可,所以注意到一个性质:如果每一行分别有没有操作已经确定,答案就确定了;因为每一列是否操作是互相独立的,如果操作能使答案变小就操作,否则就不操作。

    形式化地说,设num[x]表示x的二进制表示中1的个数,并定义

    [b[x] = min (num[x],num[2^n-x-1]) ]

    然后,每一行是否操作状压成一个数mask,初始时每一列的状态压成situ[1]~situ[m],那么对于固定的mask,答案是(sum_{i=1}^{m}b[mask igoplus situ[i]]),下记这个东西为c[mask]。由于有异或,所以我们尽量往异或卷积上凑:

    [{sumlimits_{i=1}^{m}b[mask igoplus situ[i]]} ]

    [=sumlimits_{x=0}^{2^n-1}(sumlimits_{i=1}^{m}[situ[i]=x])b[mask igoplus x] ]

    [={sumlimits_{x=0}^{2^n-1}}(sumlimits_{i=1}^{m}[situ[i]=x])sumlimits_{y=0}^{2^n-1}[xigoplus y=mask]b[y] ]

    好了,如果设(a[x]=sum_{i=1}^{m}[situ[i]=x]),式子就化简成了

    [c[mask]=sumlimits_{xigoplus y=mask}a[x]b[y] ]

    FWT结束此题。

    总时间复杂度(O(2^nn))

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    #define rg register
    #define In inline
    
    const ll N = 1048576;
    const ll M = 100000;
    
    In ll read(){
    	ll s = 0,ww = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
    	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
    	return s * ww;
    }
    
    In void calc(ll &x,ll &y,ll opt){
    	if(opt == 1){
    		ll X = x + y,Y = x - y;
    		x = X,y = Y;
    	}
    	else{
    		ll X = (x + y) >> 1,Y = (x - y) >> 1;
    		x = X,y = Y;
    	}
    }
    
    void FWT(ll a[],ll deg,ll opt){
    	for(rg int n = 2;n <= deg;n <<= 1){
    		int m = n >> 1;
    		for(rg int i = 0;i < deg;i += n)
    			for(rg int j = 0;j < m;j++)
    				calc(a[i+j],a[i+j+m],opt);
    	}
    }
    
    ll n,m,deg;
    ll a[N+5],b[N+5],c[N+5],num[N+5];
    ll situ[M+5];
    char s[M+5];
    
    int main(){
    	n = read();m = read(),deg = 1ll << n;
    	for(rg int i = 1;i <= n;i++){
    		scanf("%s",s + 1);
    		for(rg int j = 1;j <= m;j++)situ[j] = ((situ[j]<<1) | (s[j]-'0'));
    	}
    	for(rg int i = 1;i <= m;i++)a[situ[i]]++;
    	for(rg int i = 1;i < deg;i++)num[i] = num[i>>1] + (i&1);
    	for(rg int i = 0;i < deg;i++)b[i] = min(num[i],num[deg-i-1]);
    	FWT(a,deg,1);
    	FWT(b,deg,1);
    	for(rg int i = 0;i < deg;i++)c[i] = a[i] * b[i];
    	FWT(c,deg,-1);
    	ll ans = n * m;
    	for(rg int i = 0;i < deg;i++)ans = min(ans,c[i]);
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    使用iptables禁止外网访问tomcat的8080端口
    解决ie浏览器下载apk或ipa变为zip
    'ᄈ'
    centos上安装grafana
    vertica单节点故障恢复 Startup Failed, ASR Required
    vertica审计日志
    centos安装nginx1.17.9
    centos上nginx转发tcp请求
    centos 安装mysql8.0.16
    centos下导出docx为html
  • 原文地址:https://www.cnblogs.com/xh092113/p/12380895.html
Copyright © 2020-2023  润新知