2019 牛客多校 第五场 B.generator 1(十进制矩阵快速幂)

本文介绍了一种解决大数线性递推问题的方法,利用矩阵快速幂算法,解决了当n非常大(可能有百万位数)且需要进行模运算的情况。通过示例展示了如何使用此方法计算特定递推序列的第n项。

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

generator 1

题目描述 

You are given four positive integers x_0, x_1, a, bx0​,x1​,a,b. And you know x_i = a \cdot x_{i-1} + b \cdot x_{i-2}xi​=a⋅xi−1​+b⋅xi−2​ for all i \ge 2i≥2.

Given two positive integers n, and MOD, please calculate x_nxn​ modulo MOD.

Does the problem look simple? Surprise! The value of n may have many many digits!

输入描述:

The input contains two lines.
The first line contains four integers x_0, x_1, a, bx0​,x1​,a,b (1 \le x_0, x_1, a, b \le 10^91≤x0​,x1​,a,b≤109).
The second line contains two integers n, MOD (1 \le n < 10^{(10^6)}, 10^9 < MOD \le 2 \times 10^91≤n<10(106),109<MOD≤2×109, n has no leading zero).

输出描述:

Print one integer representing the answer.

示例1

输入

1 1 1 1
10 1000000001

输出

89

说明

The resulting sequence x is Fibonacci sequence. The 11-th item is 89.

示例2

输入

1315 521 20185 5452831
9999999999999999999999999999999999999 1000000007

输出

914730061

题意:f(n) = a * f(n - 1) + b * f(n - 2),给一个 n ,求 f(n) ,n 的位数最长为 1e6

思路:n 特别大,如果用二进制快速幂是会T的,因为 logn 就是 1e6,还需要算上大数取模和大数除的复杂度。

Code:

#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs (i << 1) + 1
#define fir first
#define sec second
#define CLR(a) while(!(a).empty()) a.pop()
 
using namespace std;
 
const int maxn = 3;
LL mod;
struct mat{
    LL m[maxn][maxn];
    LL n;                   //n为输入的矩阵大小
    mat(LL _Size) {
        memset(m, 0, sizeof m);
        n = _Size;
    }
    inline void set() {
        memset(m, 0, sizeof m);
        for(int i = 1;i <= n;++ i) m[i][i] = 1;
    }
    inline mat operator * (const mat &B) const{     // 计算矩阵 A * B, 放入C
        mat C = mat(n);
        for(int i = 1;i <= n;i ++)
            for(int j = 1;j <= n;j ++){
                for(int x = 1;x <= n;x ++)
                    C.m[i][j] = (C.m[i][j] + m[i][x] * B.m[x][j]) % mod;
            }
        return C;
    }
    inline mat operator ^ (LL coun){    // 矩阵快速幂, 矩阵A的 coun次幂
        mat C = mat(n),A = *this;
        C.set();
        while(coun > 0){
            if(coun & 1) C = C * A;
            A = A * A;
            coun >>= 1;
        }
        return C;
    }
};
 
mat quick_mat(mat A,char* c){
    mat C = mat(2);
    C.set();
    int t = strlen(c); -- t;
    while(t >= 0){
        LL cnt = c[t] - '0';
        mat tmp = A;
        while(cnt){
            if(cnt & 1) C = C * tmp;
            tmp = tmp * tmp;
            cnt >>= 1;
        }
        A = A ^ 10;
        -- t;
    }
    return C;
}
 
char tmp[1000010],nn[1000010];                  /// 减
int c[1000010],d[1000010],f[1000010];
 
void bigjian(char *a, char *b) {
    int i = strlen(a) - 1, j = strlen(b) - 1;
    int x = 0, flag = 1;
    for(int l = 0; l <= i; l++)
        c[l] = a[l] - '0';
    for(int l = 0; l <= j; l++)
        d[l] = b[l] - '0';
    while(j != -1) {
        if(c[i] < d[j]) {
            c[i] += 10;
            c[i - 1] -= 1;
        }
        f[x++] = c[i] - d[j];
        i--;
        j--;
    }
    while(i >= 0) {
        if(c[i] < 0) {
            c[i] += 10;
            c[i - 1] -= 1;
        }
        f[x++] = c[i];
        i--;
    }
    int p = 0;
    if(f[x - 1] == 0)
        flag = 0;
    for(int y = x - 1; y >= 0; y--) {
        if(f[y] != 0)
            flag = 1;
        if(flag == 1)
            tmp[p ++] = f[y] + '0';
    }
    if(flag == 0)
        tmp[p ++] = '0';
    tmp[p] = '\0';
}
 
int main() {
    LL x0,x1,a,b; scanf("%lld%lld%lld%lld",&x0,&x1,&a,&b);
    scanf("%s%lld",nn,&mod);
    mat A = mat(2);
    A.m[1][1] = a; A.m[1][2] = 1LL;
    A.m[2][1] = b; A.m[2][2] = 0LL;
    mat B = mat(2);
    B.m[1][1] = x1; B.m[1][2] = x0;
    bigjian(nn,"1");
    A = quick_mat(A,tmp);
    B = B * A;
    printf("%lld\n",B.m[1][1]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值