CF 551D GukiZ and Binary Operations

We all know that GukiZ often plays with arrays.

Now he is thinking about this problem: how many arrays a, of length n, with non-negative elements strictly less then 2l meet the following condition: ? Here operation means bitwise AND (in Pascal it is equivalent to and, in C/C++/Java/Python it is equivalent to &), operation means bitwise OR (in Pascal it is equivalent to , in C/C++/Java/Python it is equivalent to |).

Because the answer can be quite large, calculate it modulo m. This time GukiZ hasn't come up with solution, and needs you to help him!

Input

First and the only line of input contains four integers n, k, l, m (2 ≤ n ≤ 1018, 0 ≤ k ≤ 1018, 0 ≤ l ≤ 64, 1 ≤ m ≤ 109 + 7).

Output

In the single line print the number of arrays satisfying the condition above modulo m.

Sample test(s)
Input
2 1 2 10
Output
3
Input
2 1 1 3
Output
1
Input
3 3 2 10
Output
9
 
   
解题思路:
我们把K分解成2进制的形式,对K的每一个二进制位进行处理,当L大于K的长度的时候我们分两种情况处理,一种是L-count_bit(K)中,每一位的方案数均为长度为N的0,1序列中,不存在连续1的方案数,存在连续1的方案数为总的方案数减去连续1的方案数,对count_bit(K)的计算我们跟据K所在位是0还是1乘以相应的方案数,注意对一些例如K=0,count_bit(K) > L等情况的一些特判。
不存在连续1的方案数的计算方法是采用递推的方法:f(n) = f(n-1) + f(n-2), 思路一会儿就想出来了,但是这题得注意整数的溢出问题,我们需要将本题中所有参与的运算全部改成64位整型,我就是被这一点坑了好久,实在是蛋疼啊,这次算是长记性了,坑爹啊,该收拾东西回宿舍了。

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long ll;
ll N, K, L, M;

struct Matrix {

    ll mat[2][2];

    void clear() {
        memset(mat, 0, sizeof(mat));
    }

    void init() {
        memset(mat, 0, sizeof(mat));
        mat[0][0] = mat[1][1] = 1;
    }

    friend Matrix operator + (const Matrix &a, const Matrix &b) {
        Matrix c;
        c.clear();
        for(int i = 0; i < 2; ++i) {
            for(int j = 0; j < 2; ++j) {
                c.mat[i][j] = (a.mat[i][j] + b.mat[i][j]) % M;
            }
        }
        return c;
    }

    friend Matrix operator * (const Matrix &a, const Matrix &b) {
        Matrix c;
        c.clear();
        for(int i = 0; i < 2; ++i) {
            for(int j = 0; j < 2; ++j) {
                for(int k = 0; k < 2; ++k) {
                    c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % M;
                }
            }
        }
        return c;
    }

    friend Matrix operator ^ (Matrix a, ll x) {
        Matrix ret;
        ret.init();
        while(x) {
            if(x & 1) ret = ret * a;
            a = a * a;
            x >>= 1;
        }
        return ret;
    }
};

int count_bit(ll x) {
    int bit = 0;
    if(x == 0) return 1;
    while(x) {
        bit++;
        x >>= 1;
    }
    return bit;
}

ll mul_mod(ll a, ll b) {
    ll ret = 0;
    while(b) {
        if(b & 1) ret = (ret + a) % M;
        a = a * 2 % M;
        b >>= 1;
    }
    return ret;
}

ll power_mod(ll a, ll b) {
    ll ret = 1;
    while(b) {
        if(b&1) ret = mul_mod(ret, a);
        a = mul_mod(a, a);
        b >>= 1;
    }
    return ret;
}

int main() {

    //freopen("aa.in", "r", stdin);

    scanf("%I64d %I64d %I64d %I64d", &N, &K, &L, &M);
    if(L == 0 && K == 0) {
        printf("%I64d\n", 1LL%M);
    } else if(count_bit(K) > L) {
        printf("0\n");
    } else {
        Matrix a;
        a.mat[0][0] = 0; a.mat[0][1] = 1;
        a.mat[1][0] = 1; a.mat[1][1] = 1;
        a = a^(N-1);
        ll t1 = (1*a.mat[0][1] + 2*a.mat[1][1]) % M;
        ll t2 = ((power_mod(2LL, N) - t1) % M + M) % M;
        ll x1 = L - count_bit(K);
        ll x2 = count_bit(K);
        ll y1, y2 = 1;
        y1 = power_mod(t1, x1);
        for(int i = 0; i < x2; ++i) {
            if(K&(1LL<<i)) {  // 注意 1<<i 可能溢出, 应该改为 1LL << i
                y2 = mul_mod(y2, t2);
            } else {
                y2 = mul_mod(y2, t1);
            }
        }
        printf("%I64d\n", mul_mod(y1, y2));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值