排列组合和快速幂的应用
题意:
给你一个数t(0
≤
\leq
≤ t
≤
\leq
≤ 5)和t组数据,每组数据包含两个数n和k (1≤n≤2*
1
0
5
10^{5}
105,1≤k≤2*
1
0
5
10^{5}
105)。任选n个数
a
1
a_1
a1,
a
2
a_2
a2,
a
3
a_3
a3,
…
\dots
…,
a
n
a_n
an,每个数的范围在0到
2
k
2^{k}
2k-1,求满足
a
1
a_1
a1 &
a
2
a_2
a2 &
a
3
a_3
a3 &
…
\dots
…&
a
n
a_n
an
≥
\geq
≥
a
1
a_1
a1 ^
a
2
a_2
a2 ^
a
3
a_3
a3 ^
…
\dots
…^
a
n
a_n
an的选择方案的个数,答案对
1
0
9
10^9
109+7取模。
Example
input
3
3 1
2 1
4 0
output
5
2
1
思路:
把选择的每一个数看成k位的2进制数,左边运算结果设为L,右边结果设为R。二进制每一位的运算结果只与当前位有关,所以L、R的第i位只与每一个选择的数的第i位有关。
考虑二进制数的每一位,当前位&运算结果为1时,表示选择的所有数该位都为1,此时分为两种情况:n为奇数时,n个1相异或结果为1,n个0相异或结果为0;n为偶数时,n个1相异或结果为0,n个0相异或结果为1。
也就是说,n为奇数时,
L
i
L_i
Li
≤
\leq
≤
R
i
R_i
Ri,要让L
≥
\geq
≥R成立,只能是
L
i
L_i
Li=
R
i
R_i
Ri,分为该位为1和该位为0两种情况,该位为1时,只有一种情况,即所有数该位都为1。该位为0时,把奇数个1变为0即可;n为偶数时,n个数的第i位二进制数都是1,则
L
i
L_i
Li=1且
R
i
R_i
Ri=0,所以只要保证
L
i
+
1
L_{i+1}
Li+1,
L
i
+
2
L_{i+2}
Li+2,
L
i
+
3
L_{i+3}
Li+3,
…
\ldots
…,
L
k
L_{k}
Lk和
R
i
+
1
R_{i+1}
Ri+1,
R
i
+
2
R_{i+2}
Ri+2,
R
i
+
3
R_{i+3}
Ri+3,
…
\ldots
…,
R
k
R_{k}
Rk分别相等,而使
L
i
L_i
Li=1,
R
i
R_i
Ri=0,L>R便能成立。在n个数的第i位二进制数都是1的基础上把任意偶数个数的第i位变成0,则
L
i
L_i
Li=
R
i
R_i
Ri=0。
综上所述,n为奇数时,第i个二进制位的选择有
2
n
−
1
+
1
2^{n-1}+1
2n−1+1种,一共k位,总共方案数为
(
2
n
−
1
+
1
)
k
(2^{n-1}+1)^{k}
(2n−1+1)k;类比n为奇数的情况,n为偶数时,L=R=0的方案数有
(
2
n
−
1
−
1
)
k
(2^{n-1}-1)^{k}
(2n−1−1)k种,L>R的方案数有
∑
i
=
1
k
(
2
n
−
1
−
1
)
k
−
i
∗
(
2
n
)
i
−
1
\sum_{i=1}^k(2^{n-1}-1)^{k-i}*(2^n)^{i-1}
∑i=1k(2n−1−1)k−i∗(2n)i−1种。
代码:
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int main(){
int t;
cin>>t;
while(t--){
int n,k;
cin>>n>>k;
ll p=qpow(2,n-1);
if(n&1){
cout<<qpow(p+1,k)<<endl;
continue;
}
else{
ll res=qpow(p-1,k),s=(p<<1)%mod;
for(int i=1;i<=k;i++){
res=(res+qpow(p-1,k-i)*qpow(s,i-1)%mod)%mod;
}
cout<<res<<endl;
}
}
return 0;
}