CF662C - Binary Table(fwt,dp)

题目

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

题解

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

\[g_j=\sum\limits_{i=1}^{m}{\min(f_{a_i\bigoplus j},n-f_{a_i\bigoplus 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_{i\bigoplus j} \cdot f_j} \]

直接使用fwt解决。

#include <bits/stdc++.h>

#define endl '\n'
#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;
}
posted @ 2021-09-14 20:19  limil  阅读(28)  评论(0编辑  收藏  举报