zoukankan      html  css  js  c++  java
  • CF662C

    题目

    给定一个(n imes m)的01矩阵,你每次可以翻转一行或一列任意次。问操作若干次后矩阵中最少的1是多少?(nle 20)(m le 100000)

    题解

    (n)很小,所以可以将每一列状压为(a_i)。若干次操作后,行操作集合相当于一个掩码,列操作相当于是否对掩码取反然后异或到(a_i)上。设(f_i)代表(i)的二进制表示中1的个数,(g_i)代表列操作对应的掩码为(i)时的最小值。

    [g_j=sumlimits_{i=1}^{m}{min(f_{a_iigoplus j},n-f_{a_iigoplus j})} ]

    则答案为(min(g_i))

    时间复杂度为(O(2^{2n})),显然超时。为了快速计算(g),设(h_i)代表序列(a)中值为(i)的个数,(cnt_i)值为(i)时最少的1的个数,有

    [cnt_i=min(f_{i},n-f_{i}) ]

    [g_i=sum{cnt_{iigoplus j} cdot f_j} ]

    直接使用fwt解决。

    #include <bits/stdc++.h>
    
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
    
    using namespace std;
    /*-----------------------------------------------------------------*/
    
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
    
    const int N = 3e6 + 10;
    const double eps = 1e-5;
    
    ll f1[N], f2[N];
    
    void fwt_xor(ll f[], int len) {
    	for(int l = 2; l <= len; l <<= 1) {
    		for(int i = 0, p = (l >> 1); i < len; i += l) {
    			for(int j = i; j < i + p; j++) {
    				ll a0 = f[j], a1 = f[j + p];
    				f[j] = a0 + a1;
    				f[j + p] = a0 - a1;
    			}
    		}
    	}
    }
    
    void ifwt_xor(ll f[], int len) {
    	for(int l = 2; l <= len; l <<= 1) {
    		for(int i = 0, p = (l >> 1); i < len; i += l) {
    			for(int j = i; j < i + p; j++) {
    				ll a0 = f[j], a1 = f[j + p];
    				f[j] = (a0 + a1) / 2;
    				f[j + p] = (a0 - a1) / 2;
    			}
    		}
    	}
    }
    
    int arr[N];
    
    int count(int x) {
    	int res = 0;
    	while(x) {
    		if(x & 1) res++;
    		x >>= 1;
    	}
    	return res;
    }
    
    int main() {
    	IOS;
    	int n, m;
    	cin >> n >> m;
    	for(int i = 1; i <= n; i++) {
    		for(int j = 1; j <= m; j++) {
    			char ch;
    			cin >> ch;
    			arr[j] += (ch - '0') << (i - 1);
    		}
    	}
    	for(int i = 1; i <= m; i++) {
    		f1[arr[i]]++;
    	}	
    	for(int i = 0; i < (1 << n); i++) {
    		int num = count(i);
    		f2[i] = min(num, n - num);
    	}
    	fwt_xor(f1, 1 << n);
    	fwt_xor(f2, 1 << n);
    	for(int i = 0; i < (1 << n); i++) f1[i] = f1[i] * f2[i];
    	ifwt_xor(f1, 1 << n);
    	ll ans = f1[0];
    	for(int i = 1; i < (1 << n); i++) ans = min(ans, f1[i]);
    	cout << ans << endl;
    }
    
  • 相关阅读:
    Android Studio学习笔记(1)
    2019全国大学生电子设计大赛总结
    包与常用模块
    模块
    迭代器、生成器与递归调用
    叠加多个装饰器与有参数的装饰器。
    装饰器
    控制指针的移动、函数
    字符编码
    python 数据类型之列表、元组、字典、集合
  • 原文地址:https://www.cnblogs.com/limil/p/15269427.html
Copyright © 2011-2022 走看看