CF662C Binary Table (快速沃尔什变换FWT)

题面

题解

我们会发现,如果单独的一列或一行,它的答案是O1确定的,如果确定了每一行是否变换,那么最后的答案也就简单了许多,

如果确定了行的变换状压下来是x(即x的i位表示第i行是否变换,理解就行),那么每列的状态就要异或x,这没问题吧

接着,其实它的行列是可以交换顺序的,对答案没有影响,状态一定的列的最终贡献都一样,

所以就把状态为 i 的列的数量记为 c[i],方便计算,

然后,提前预处理出列的状态为 i 时单独考虑的答案 f[i](即反转或不反转的最小1数量),方便计算

若枚举行的变换x,那么

Ans[x]=\sum_i f[i]\sum_j c[j] \;(j\;xor\;x==i)

注意,xor的逆运算是xor

Ans[x]=\sum_i f[i]\sum_j c[j] \;(i\;xor\;j==x)=\sum_{i,j} f[i]* c[j] \;(i\;xor\;j==x)

于是,就转变成了卷积,用FWT就行了,最后枚举x算最小值完事

害怕答案会爆的朋友也可以使用取模,由于最终的答案肯定不会超过1e6,所以你的模数只要超过1e6就没有影响答案。

CODE

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#define MAXN (1<<20|5)
#define LL long long
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
#define rg register
#pragma GCC optimize(2)
//#pragma G++ optimize(3) 
//#define int LL
char char_read_before = 1;
inline int read() {
	int f = 1,x = 0;char s = char_read_before;
	while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 - '0' + s;s = getchar();}
	char_read_before = s;return x * f;
}
inline int readone() {
	int x = 0;char s = getchar();
	while(s < '0' || s > '9') s = getchar();
	char_read_before = 1;return s - '0';
}
int zxy = 1000000007; // 用来膜的
int inv2 = (zxy+1)/2;
inline int qm(LL x,int dalao) {return x >= dalao ? qm(x-dalao,dalao):x;}
int n,m,i,j,s,o,k;
inline void DWTXOR(int *s,int m) {
	for(int k = m;k > 1;k >>= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j] = qm((s0 +0ll+ zxy - s1) , zxy);
				s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy);
			}
		}
	}
	return ;
}
inline void IDWTXOR(int *s,int m) {
	for(int k = 2;k <= m;k <<= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy) *1ll* inv2 % zxy;
				s[j] = qm((s0 +0ll+ zxy - s1) , zxy) *1ll* inv2 % zxy;
			}
		}
	}
	return ;
}
inline void DWTOR(int *s,int m) {
	for(int k = m;k > 1;k >>= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j] = qm((s0 +0ll+ s1) , zxy);
			}
		}
	}
	return ;
}
inline void IDWTOR(int *s,int m) {
	for(int k = 2;k <= m;k <<= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j] = qm((s1 +0ll+ zxy - s0) , zxy);
			}
		}
	}
	return ;
}
inline void DWTAND(int *s,int m) {
	for(int k = m;k > 1;k >>= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				LL s0 = s[j-(k>>1)],s1 = s[j];
				s[j-(k>>1)] = qm((s0 +0ll+ s1) , zxy);
			}
		}
	}
	return ;
}
inline void IDWTAND(int *s,int m) {
	for(int k = 2;k <= m;k <<= 1) {
		for(int i = 0;i < m;i += k) {
			for(int j = i+(k>>1);j < i+k;j ++) {
				int s0 = s[j-(k>>1)],s1 = s[j];
				s[j-(k>>1)] = qm((s0 +0ll+ zxy - s1) , zxy);
			}
		}
	}
	return ;
}
int A[MAXN],B[MAXN],as[MAXN];
int ct[MAXN],a[MAXN];
int main() {
	n = read();m = read();
	int M = 1<<n;
	for(int i = 1;i < M;i ++) {
		ct[i] = ct[i ^ lowbit(i)] + 1;
		A[i] = min(ct[i],n - ct[i]);
	}
	for(int i = 0;i < n;i ++) {
		for(int j = 1;j <= m;j ++) {
			s = readone();
			a[j] |= (s<<i);
		}
	}
	for(int i = 1;i <= m;i ++) B[a[i]] ++;
	DWTXOR(A,M);DWTXOR(B,M);
	for(int i = 0;i < M;i ++) as[i] = A[i] *1ll* B[i] % zxy;
	IDWTXOR(as,M);
	int ans = 0x7f7f7f7f;
	for(int i = 0;i < M;i ++) ans = min(ans,as[i]);
	printf("%d\n",ans);
	return 0;
} 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值