CF662C Binary Table
FWT板子w
就是一个显然的做法就是枚举哪些行翻转,然后对于每一列贪心取翻转或者不翻即min(count(i),n-count(i))
这样肯定是过不去的 我们来考虑优化
我们记录数组F表示对于一个数i它的较优翻转 即上面的那个柿子
然后再记录一个数组表示原来的矩阵中每一列的计数 cnt[i]表示将一列看成一个二进制数 这个数是i的列数
对于答案我们有就是考虑计算贡献 ans[k]表示行的翻转是k
由于是等价于的所以我们可以转化成卷积形式然后套上异或FWT即可
【码风日益毒瘤】代码。
//Love and Freedom.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mxn (1<<20)
#define int ll
using namespace std;
int f[mxn+10],cnt[mxn+10];
int ans[mxn+10];
void fwt(int *a,int n,int f)
{
for(int k=2,mid=1;k<=n;k<<=1,mid<<=1)
{
for(int i=0;i<n;i+=k) for(int j=0;j<mid;j++)
{
int x=a[i+j],y=a[i+mid+j];
if(f>0) a[i+j] = x+y,a[i+mid+j] = x-y;
else a[i+j] = (x+y)/2,a[i+mid+j] = (x-y)/2;
}
}
}
char ch[100010];
int mp[21][100010];
int count(int x)
{
int ct=0;
while(x) x=x&(x-1),ct++;
return ct;
}
signed main()
{
int n,m;
scanf("%lld%lld",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%s",ch);
for(int j=0;j<m;j++)
mp[i][j]=ch[j]-'0';
}
for(int i=0;i<m;i++)
{
int tmp=0;
for(int j=0;j<n;j++)
tmp|=mp[j][i],tmp<<=1;
tmp>>=1; cnt[tmp]++;
}
int qwq=0,top=1<<n;
for(int i=0;i<top;i++) qwq=count(i),f[i]=min(qwq,n-qwq);
fwt(f,top,1); fwt(cnt,top,1);
for(int i=0;i<top;i++) ans[i] = f[i] * cnt[i];
fwt(ans,top,-1);
int fin=n*m;
for(int i=0;i<top;i++) fin=min(fin,ans[i]);
printf("%lld\n",fin);
return 0;
}