1557C Moamen and XOR

题目链接

题意

给出非负整数 n,k,构造长度为 n 的数组 a ,使得对任意的 i , a 1 a_1 a1 < 2k都成立,并且数组 a 满足在这里插入图片描述
求合法的数组 a 的个数,答案对1 000 000 007取模。

思路

  • 利用容斥思想,考虑不合法的情况,即小于的情况(因为这样不用考虑等于的情况)。
  • 因为是二进制运算,所以可以先考虑一位的情况,通过分析发现:
1&1=1  >  1^1=0
1&0=0  <  1^0=1
0&0=0  =  0^0=0
  • 然后可以得出结论:
1.当n为奇数且这n个数的这一位都是1时,&的结果 > ^的结果
2.当n为偶数且这n个数的这一位都是1时,&的结果 = ^的结果
3.当这n个数的这一位中有偶数个1并且有0时,&的结果 = ^的结果
4.当这n个数的这一位中有奇数个1并且有0时,&的结果 < ^的结果
  • 然后分两种情况考虑:

    • 当前位的 &的结果^的结果

      • n为奇数时,方案种数为 C n 1 C^1_n Cn1+ C n 3 C^3_n Cn3+ C n 5 C^5_n Cn5+···+ C n n − 2 C^{n-2}_n Cnn2=2n-1-1
      • n为偶数时,方案种数为 C n 1 C^1_n Cn1+ C n 3 C^3_n Cn3+ C n 5 C^5_n Cn5+···+ C n n − 1 C^{n-1}_n Cnn1=2n-1
    • 当前位的 &的结果= ^的结果

      • n为奇数时,方案种数为1+ C n 2 C^2_n Cn2+ C n 4 C^4_n Cn4+ C n 6 C^6_n Cn6+···+ C n n − 1 C^{n-1}_n Cnn1=2n-1+1
      • n为奇数时,方案种数为 C n 2 C^2_n Cn2+ C n 4 C^4_n Cn4+ C n 6 C^6_n Cn6+···+ C n n − 2 C^{n-2}_n Cnn2=2n-1-1
  • 以上利用了组合数的性质 C n 1 C^1_n Cn1+ C n 3 C^3_n Cn3+ C n 5 C^5_n Cn5+···= C n 2 C^2_n Cn2+ C n 4 C^4_n Cn4+ C n 6 C^6_n Cn6+···=2n-1
  • 要想 &的结果^的结果 ,那么必定存在某一位m,使得比m位高的位&的结果^的结果都相等,比m位低的位不用考虑。
  • 那么
    • n为奇数时方案数为(2n-1+1)m ×(2n-1-1)×(2n)k-m-1%p.
    • n为偶数时方案数为(2n-1-1)m ×2n-1×(2n)k-m-1%p.
  • 枚举m,把答案相加即是所有的不合法的方案数。
  • 最后用总数(2n)k减去即可。

注意事项

  • long long
  • 特判k=0
  • (2n)k不要写成2nk

代码

#include<cstdio>
#define int long long
#define ri register int
using namespace std;
const int p=1e9+7;
int read(){//快读
    int x=0,f=0;char c=getchar();
    while(c>'9'||c<'0') f=c=='-',c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
    return f?-x:x;
}
int ksm(int a,int b){//快速幂
    a%=p,b%=p;
    int ans=1;
    while(b){
        if(b&1) ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
void solve(){
    int n=read(),k=read(),ans=0;
    if(!k){//注意特判
        printf("1\n");
        return;
    }
    int tmp=ksm(2,n-1);
    if(n&1)//奇数
        for(ri i=0;i<k;++i)
            ans=(ans+ksm(tmp+1,i)*(tmp-1)%p*ksm(tmp*2,k-i-1)%p)%p;
    else//偶数
        for(ri i=0;i<k;++i)
            ans=(ans+ksm(tmp-1,i)*tmp%p*ksm(tmp*2,k-i-1)%p)%p;
    ans=(ksm(tmp*2,k)-ans+p)%p;//最终答案
    printf("%lld\n",ans);
}
#undef int
int main(){
    int T=read();
    while(T--) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Robin_w2321

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值