【bitmask】牛客挑战赛 B 清新题

B-[NCT058B]清新题_牛客挑战赛58 (nowcoder.com)

题意:

思路:

我们先考虑左边大于右边的情况

再考虑左边等于右边的情况

首先,一个数大于另一个数,以位运算的视角来看就是:从高位到低位的第一个不同位,大的那个数第一个不同位是1,小的那个数不同位是0

 那么,我们设第一个不同位是第i位

我们去枚举这个不同位的位置i,从1枚举到m

所以首先,我们保证最高位到第i-1位两个数的位都相同:

考虑让左边n个数的第i-1位随机排列,就有2^n种组合

然后让右边前n-1个数的第i-1位随机排列,就有2*(n-1)种组合

由于右边的操作是异或,因此最后一位是0还是1决定了右边的式子异或的结果是1还是0,因为n个数异或的结果的每一位就是每一位的1的个数的奇偶性,最后一位决定了最后一位的奇偶性,因此最后一位决定了右边n个数异或的结果

因此这样组合就有(2^n)*(2^(n-1))种方案

因为我们还要枚最低位i的位置,因此保证前n-1个数相同的方案数就是((2^n)*(2^(n-1)))^(i-1)种

然后,保证对于第i位,大的那个数为1,小的那个数为0:

左边因为是或的结果,因此要为1的话只需除去全为0的情况就好了,方案数为(2^n)-1

类似地,右边的方案数为2^(n-1)

所以方案数为((2^n)-1)*(2^(n-1))

第i位后面的数可以随便排列:

方案数为2^((m-i)*2*n)

所以左边大于右边的方案数就是把三个数加起来就行

即:ans+=((2^n)*(2^(n-1)))^(i-1)+((2^n)-1)*(2^(n-1))+2^((m-i)*2*n),i=1……m

然后再加上左边等于右边的情况:

对于每一位i,都让左边n个数的第i位随机排列,让右边n-1个数的第i位随机排列,第n个数控制相同,因此方案数为2^(2*n*m-m)

Code:

#include<bits/stdc++.h>
using namespace std;
const int P=1e9+7;
#define ll long long
ll quick_pow(ll x,ll k){
    ll ans=1;
    while(k){
        if(k&1) ans=ans*x%P;
        x=x*x%P;
        k>>=1;
    }
    return ans;
}
int main(void){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    ll n,m;
    scanf("%lld%lld",&n,&m);
    ll p1=quick_pow(2,n-1);
    ll p2=quick_pow(2,n);
    ll p=(p2-1+P)%P;
    ll q;
    ll ans=0;
    for(int i=1;i<=m;i++) {
        q=2*n*m-n-i;
        q%=P-1;
        ans+=quick_pow(2,q)*p%P;
        ans%=P;
    }
    ans+=quick_pow(2,2*n*m-m);
    ans%=P;
    printf("%lld\n",ans); 
}

总结:

思维:

bitmask:

1.一个数大于另一个数,就找它们从最高位到最低位第一个不同的位,让大的那个数的这一位变成1,让小的那个数这一位为0

2.关于异或和:异或和的结果往往可以由其中一个数决定,因为其奇偶性可以由一个数改变

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值