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.关于异或和:异或和的结果往往可以由其中一个数决定,因为其奇偶性可以由一个数改变