GukiZ and Binary Operations
分类:
matrices
combinatorics
math
number theory
1.题意概述
- 给你一个长度为n的数组 a[1...n] ,满足 a[i]<2n ,并且有 (a1 and a2) or (a2 and a3) or...or (an−1 and an)=k ,答案取膜m
2.解题思路
- 考虑它的二进制位,独立计算每个位,假设
dp[i]
表示长度为i的序列为0的种数,那么容易得到递推式为 dp[i]=dp[i−1]+dp[i−2] ,这就类似于斐波那契数列,因为是单组用例,我们可以考虑直接map存然后去递推,当然最正统的做法是考虑用矩阵快速幂加速地去递推:
[dp[i]dp[i−1]]=[1110]×[dp[i−1]dp[i−2]]
3.AC代码
ll mod;
map<ll, ll> F;
ll quickmod(ll a, ll b, ll m) {
ll ans = 1;
while(b) { //用一个循环从右到左便利b的所有二进制位
if(b & 1) { //判断此时b[i]的二进制位是否为1
ans = (ans * a) % m; //乘到结果上,这里a是a^(2^i)%m
b--;//把该为变0
}
b /= 2;
a = a * a % m;
}
return ans;
}
ll Fib(ll n) {
if(F.count(n))
return F[n];
ll k = n / 2;
if(n % 2 == 0)
return F[n] = (Fib(k) * Fib(k) % mod + Fib(k - 1) * Fib(k - 1) % mod) % mod;
return F[n] = (Fib(k) * Fib(k + 1) % mod + Fib(k - 1) * Fib(k) % mod) % mod;
}
int main() {
ll n, k, l;
scanf("%I64d%I64d%I64d%I64d", &n, &k, &l, &mod);
if(mod == 1 || k >= (1ULL << min(l, 63ll)) ) {
puts("0");
} else {
F[0] = 1, F[1] = 1;
ll res = 1;
ll zero = Fib(n + 1);
ll all = quickmod(2, n, mod);
ll temp = (all - zero + mod) % mod;
rep(i, 0, l) {
if(k & 1)
res = res * temp % mod;
else res = res * zero % mod;
k >>= 1;
}
// printf("%I64d\n", all);
printf("%I64d\n", res);
}
return 0;
}