Codeforces #663E: Binary Table 题解

这篇博客介绍了如何解决Codeforces上的#663E问题,重点在于利用位运算和状态压缩来优化解决方案。通过枚举行的翻转状态并计算每列在翻转后的最少1的数量,博主提供了详细的理解和公式解析。
摘要由CSDN通过智能技术生成

很好的题目
看到列很多但是行很少,我们显然想到状压行,考虑枚举一个 Mask M a s k 表示行的翻转状态,第i位是1表示翻转了第i行,反之亦然
这样枚举完了以后,设第i列原来的状态是 mask m a s k ,那么现在的状态就是 Maskmask M a s k ⨁ m a s k ,这时我们考虑是否需要翻转第i列的时候,只要考虑 Maskmask M a s k ⨁ m a s k !(Maskmask) ! ( M a s k ⨁ m a s k ) 中哪个1比较少就可以了
形式化的,记 F(mask) F ( m a s k ) 表示原来的m列中有多少列的值是 mask m a s k ,记 G(mask) G ( m a s k ) 表示如果考虑过行的翻转后这列的状态是 mask m a s k ,这列最终最少能有多少个1,即 G(mask)=min(buitinpopcount(mask),nbuiltinpopcount(mask)) G ( m a s k ) = m i n ( b u i t i n − p o p c o u n t ( m a s k ) , n − b u i l t i n − p o p c o u n t ( m a s k ) )
我们的目标是计算:

minMask=02n1mask=02n1F(mask)G(Maskmask) min M a s k = 0 2 n − 1 ∑ m a s k = 0 2 n − 1 F ( m a s k ) ∗ G ( M a s k ⨁ m a s k )

然后我们很开心的发现这样的复杂度是 O(22n) O ( 2 2 n ) ,会超时
尝试重写上面的式子,记 res=Maskmask r e s = M a s k ⨁ m a s k
minMask=02n1mask=02n1F(mask)G(Maskmask) min M a s k = 0 2 n − 1 ∑ m a s k = 0 2 n − 1 F ( m a s k ) ∗ G ( M a s k ⨁ m a s k )

=minMask=02n1mask=02n1res=02n1[Maskmask=res]F(mask)G(res) = min M a s k = 0 2 n − 1 ∑ m a s k = 0 2 n − 1 ∑ r e s = 0 2 n − 1 [ M a s k ⨁ m a s k = r e s ] ∗ F ( m a s k ) ∗ G ( r e s )

考虑到异或的优良性质,中括号内的等式内的元素是可以换顺序的,即
=minMask=02n1mask=02n1res=02n1[resmask=Mask]F(mask)G(res) = min M a s k = 0 2 n − 1 ∑ m a s k = 0 2 n − 1 ∑ r e s = 0 2 n − 1 [ r e s ⨁ m a s k = M a s k ] ∗ F ( m a s k ) ∗ G ( r e s )

还不够明显,换个写法
=minMask=02n1maskres=MaskF(mask)G(res) = min M a s k = 0 2 n − 1 ∑ m a s k ⨁ r e s = M a s k F ( m a s k ) ∗ G ( r e s )

很好,这就是一个异或卷积,所以可以FWT
这样总复杂度为 O(2nlog(2n)) O ( 2 n l o g ( 2 n ) ) ,即 O(n2n) O ( n 2 n )

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=3.14159265;

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

int n,m,len;
char s[22][100048];

LL a[2000048],b[2000048];

inline void FWT(LL c[],int fl)
{
    int clen,j,k;
    for (clen=2;clen<=len;clen<<=1)
        for (j=0;j<len;j+=clen)
            for (k=j;k<j+(clen>>1);k++)
            {
                LL tmp1=c[k],tmp2=c[k+(clen>>1)];
                if (fl==1)
                    c[k]=tmp1+tmp2,c[k+(clen>>1)]=tmp1-tmp2;
                else
                    c[k]=((tmp1+tmp2)>>1),c[k+(clen>>1)]=((tmp1-tmp2)>>1);
            }
}

inline void calc_FWT()
{
    len=(1<<n);
    FWT(a,1);FWT(b,1);
    for (register int i=0;i<=(1<<n)-1;i++) a[i]*=b[i];
    FWT(a,-1);
}

int main ()
{
    int i,j,Mask;
    n=getint();m=getint();
    for (i=1;i<=n;i++) scanf("%s",s[i]+1);
    for (j=1;j<=m;j++)
    {
        Mask=0;
        for (i=1;i<=n;i++) Mask|=((s[i][j]-'0')<<(i-1));
        a[Mask]++;
    }
    for (Mask=0;Mask<=(1<<n)-1;Mask++)
        b[Mask]=min(__builtin_popcount(Mask),n-__builtin_popcount(Mask));
    calc_FWT();
    LL ans=LINF;
    for (i=0;i<=(1<<n)-1;i++) ans=min(ans,a[i]);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值