CF 551D GukiZ and Binary Operations

本文深入探讨了一个关于数组长度、非负元素限制、位操作条件及模运算的应用问题。通过实例输入解析,阐述了解决此类问题的步骤与关键点,包括如何利用二进制分解和动态规划思想进行高效计算。文章还详细介绍了算法的实现过程,包括模运算的特殊注意事项和避免整数溢出的方法,旨在提供一种实用且易于理解的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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、付费专栏及课程。

余额充值