CodeForces 663E - Binary Table

给出一个 n(n20) m(m105) 列的 01 矩阵。每次操作可以将某一行取反或者将某一列取反。要求操作后的矩阵中的 1 的个数最少,求最小个数。


因为行比较少,我们考虑状压。

状压每一列的状态,sta i 位为1表示第 i 行为1,为 0 表示第i行为 1

用一个函数F(sta)表示状态为 sta 的列的数量

考虑状压我们操作了哪些行, mask i 位为1表示我们翻转了第 i 行,为0表示没有翻转第 i

考虑一个mask sta 的影响,显然操作后的结果为 masksta ,不妨记为 res

然后对一个 res ,我们可以直接计算这一列的 1 的个数和取反后1的个数并且选择一个比较小的作为结果。

不妨预处理出来一个函数 G(res) 表示 res 这样的一列在翻至多一次之后的最小的 1 的个数

对于m 105 ,我们考虑优化一下

构造一个函数 H(mask) ,表示操作为mask的时候 1 的个数。不难想到H(mask)=sta=02n1F(sta)×G(stamask)

这个看起来好像不是很好优化,我们换一种写法
H(mask)=sta=02n1res=02n1[stamask=res]F(sta)×G(res)

因为异或的一些优良的性质,我们也可以写成

H(mask)=sta=02n1res=02n1[stares=mask]F(sta)×G(res)

这不就是异或卷积嘛(╯‵□′)╯︵┻━┻

然后就做完了


具体见代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 112345;
#define LL long long 
const LL INFF = 0x3f3f3f3f3f3f3f3fll;

void fwt (LL a[] , int n ,bool on) {
    for ( int d = 1 ; d < n ; d <<= 1 ) {
        for ( int k = d << 1 , i = 0 ; i < n ; i += k ) {
            for ( int j = 0 ; j < d ; ++ j ) {
                LL x = a[i + j] , y = a[i + j + d] ;
                if(on){
                    a[i + j] = ( x + y )  ;
                    a[i + j + d] = ( x - y  )  ;
                }
                else{
                    a[i + j] = ( x + y ) / 2 ;
                    a[i + j + d] = ( x - y ) / 2;
                }
            }
        }
    }
}

char inp[22][maxn];
int arr[maxn];
LL A[1<<22],B[1<<22];

int getone(int x,int n){
    int ret = 0 ;
    while(x){
        ret += x & 1;
        x >>= 1;
    }
    return min(ret,n - ret);
}

int main(){
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i = 0 ; i < n; i ++) scanf("%s",inp[i]);
    for(int i = 0 ; i < m ; i++){
        arr[i] = 0;
        for(int j = 0 ; j < n ; j ++){
            arr[i] |= (inp[j][i] - '0') << j;
        }
    }
    memset(A,0,sizeof(A));
    for(int i = 0 ; i < m ; i++) A[arr[i]] ++;
    for(int i = 0 ; i < (1<<n) ; i ++){
        B[i] = getone(i,n);
    }
    fwt(A,1<<n,true);
    fwt(B,1<<n,true);
    for(int i = 0; i < (1<<n);i++) A[i] *= B[i];
    fwt(A,1<<n,false);
    LL ans = INFF;
    for(int i = 0 ; i < (1<<n);i++) ans = min(ans,A[i]);
    printf("%I64d\n",ans);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值