Codeforces 662 C. Binary Table

时间:2021-08-04 19:25:55

http://codeforces.com/contest/662/problem/C

题意:
n行m列01矩阵,每次可以反转一行或一列,问最后最少可以剩下多少个1

n只有20,把行状态压缩

操作奇数次相当于1次,操作偶数次相当于不操作

所以可以枚举对行的操作,将操作也状态压缩

A[i] 表示有多少列的状态为i

B[i] 表示 状态为i时的最优解,即fanzh

C[i] 表示 操作i的最优解

执行一次行操作相当于给某一列的状态异或上操作的状态

C[opt] = Σ A[state]*B[opt xor state]

(先执行这种行操作,如果某一列发现再执行一次列操作更优,那这一列再执行列操作,这通通包含在B数组里)

目前求C的复杂度为 2^2n

令 res = opt xor state

C[opt] = Σ(state) Σ(res)   [state xor res == opt] A[state]*B[res]

C[opt] = Σ(state) Σ(res)   [state xor opt == res] A[state]*B[res]

然后就可以用FWT 优化 成2^n * n

这还有个用子集反演的,是什么啊??

http://blog.csdn.net/QWsin/article/details/55054071

#include<cstdio>
#include<algorithm> using namespace std; typedef long long LL; char s[][]; LL a[],b[],c[]; int count(int i)
{
int sum=;
while(i) sum+=i&,i>>=;
return sum;
} void FWT_xor(LL *a,int n)
{
LL x,y;
for(int d=;d<n;d<<=)
for(int m=d<<,i=;i<n;i+=m)
for(int j=;j<d;++j)
{
x=a[i+j]; y=a[i+j+d];
a[i+j]=x+y; a[i+j+d]=x-y;
}
} void IFWT_xor(LL *a,int n)
{
LL x,y;
for(int d=;d<n;d<<=)
for(int m=d<<,i=;i<n;i+=m)
for(int j=;j<d;++j)
{
x=a[i+j]; y=a[i+j+d];
a[i+j]=x+y>>; a[i+j+d]=x-y>>;
}
} int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i) scanf("%s",s[i]+);
int state;
for(int i=;i<=m;++i)
{
state=;
for(int j=;j<=n;++j)
if(s[j][i]=='') state|=<<j-;
++a[state];
}
int S=<<n,sum;
for(int i=;i<S;++i)
{
sum=count(i);
b[i]=min(sum,n-sum);
}
FWT_xor(a,S);
FWT_xor(b,S);
for(int i=;i<S;++i) c[i]=a[i]*b[i];
IFWT_xor(c,S);
LL ans=n*m;
for(int i=;i<S;++i) ans=min(ans,c[i]);
printf("%I64d",ans);
}