题意
给出非负整数 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 Cnn−2=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 Cnn−1=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 Cnn−1=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 Cnn−2=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;
}