「CF662C」 Binary Table
Posted henryhuang-never-settle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「CF662C」 Binary Table相关的知识,希望对你有一定的参考价值。
「CF662C」 Binary Table
题目所给的 (n) 很小,于是我们可以考虑这样一种朴素做法:暴力枚举第 (i) 行是否翻转,这样每一行的状态就确定了,这时取每一列 (0/1) 个数较小的数字即可(因为每一列也可以翻转)。这样的时间复杂度是 (O(mcdot2^n))。
但是显然这样过不了。
我们发现表格的具体行列对我们的答案是没有影响的。即我们只需要知道状态为 (x) 的行或者状态为 (x) 的列的个数即可。由于 (nle20),这启发我们对于每一列进行状态压缩。
于是我们定义:(f_S) 表示列状态为 (S) 的列的个数,同时定义 (g_S) 为 列状态为 (S) 的列中 (0/1) 个数中小的一个。显然 (f,g) 两个数组都可以在可以接受的时间范围内预处理出来。
对于翻转情况,我们用一个数 (x) 表示,若二进制下第 (i) 位为 (1), 则代表第 (i) 行翻转。
于是有当翻转状态为 (x) 时,有
[
Ans_x=sum_{i=0}^{2^n-1}f_ig_{ioplus x}
]
其中 (oplus) 代表按位异或。
简单变形,我们可以得到
[
Ans_x=sum_{i=0}^{2^n-1}sum_{j=0}^{2^n-1}[ioplus x=j]f_ig_j
]
由于异或具有交换律,于是有
[
Ans_x=sum_{i=0}^{2^n-1}sum_{j=0}^{2^n-1}[ioplus j=x]f_ig_j
]
这就是集合幂卷积中异或卷积的标准形式,直接使用 ( exttt{FWT}) 优化计算即可。时间复杂度为 (O(n2^n))。
代码很简洁,如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
long long num[maxn],f[maxn],g[maxn];
int n,m;
inline void XOR(long long *f,int tmp){
for(int o=2,k=1;o<=n;o<<=1,k<<=1){
for(int i=0;i<n;i+=o){
for(int j=0;j<k;++j){
long long a=f[i+j],b=f[i+j+k];
if(tmp) f[i+j]=a+b,f[i+j+k]=a-b;
else f[i+j]=(a+b)>>1,f[i+j+k]=(a-b)>>1;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
int x;scanf("%1d",&x);
num[j]+=x*(1<<i-1);
}
}
for(int i=1;i<=m;++i) ++f[num[i]];
for(int i=1;i<(1<<n);++i){
int x=i;
while(x) x=(x-1)&x,++g[i];
g[i]=min(g[i],n-g[i]);
}
n=(1<<n);
XOR(f,1),XOR(g,1);
for(int i=0;i<n;++i) f[i]=f[i]*g[i];
XOR(f,0);
long long ans=(1<<30);
for(int i=0;i<n;++i) ans=min(ans,f[i]);
printf("%lld
",ans);
return 0;
}
以上是关于「CF662C」 Binary Table的主要内容,如果未能解决你的问题,请参考以下文章