数列(矩阵加速,斐波那契变式)

文章目录


题目

描述
TR非常喜欢数学,经常一个人拿出草稿纸研究奇奇怪怪的数学问题,最近,他突然对数列产生了兴趣,他找到一个数列,类似于斐波拉契,即: T n = 1 ∗ f 1 + 2 ∗ f 2 + 3 ∗ f 3 + … + n ∗ f n T_n=1*f_1+2*f_2+3*f_3+…+n*f_n Tn=1f1+2f2+3f3++nfn (fn为斐波拉契的第n项值)现在TR想请你帮忙求Tn%m的值
输入
两个用空格隔开的整数 n n n m m m 1 ≤ n , m ≤ 2 31 − 1 1 \leq n,m \leq2^{31}-1 1n,m2311
输出
T n ( m o d m ) T_n \pmod m Tn(modm)的值
样例输入
5 5
样例输出
1

思路

按照矩阵的常规递推求和思路尿性 ,首先在原矩阵中定义一个 T T T来表示和,至于下标是多少,待会再来定吧,再看,我们肯定要递推一下斐波那契,所以还要定义两个f,来递推斐波。要得到T,肯定要得到i*f[i],我们定 q [ i ] = i ∗ f [ i ] q[i] = i*f[i] q[i]=if[i],则有 q [ i ] = i ∗ f [ i ] , q [ i − 1 ] = ( i − 1 ) ∗ f [ i − 1 ] , q [ i − 2 ] = ( i − 2 ) ∗ f [ i − 2 ] q[i]=i*f[i],q[i-1]=(i-1)*f[i-1],q[i-2]=(i-2)*f[i-2] q[i]=if[i]q[i1]=(i1)f[i1]q[i2]=(i2)f[i2]我们来递推一下 q [ i ] , q [ i − 1 ] , q [ i − 2 ] q[i],q[i-1],q[i-2] q[i]q[i1]q[i2]的关系, q [ i − 1 ] + q [ i − 2 ] = ( i − 1 ) ∗ f [ i − 1 ] + ( i − 2 ) ∗ f [ i − 2 ] = q[i-1]+q[i-2]=(i-1)*f[i-1]+(i-2)*f[i-2]= q[i1]+q[i2]=(i1)f[i1]+(i2)f[i2]=
( i − 2 ) ∗ ( f [ i − 1 ] + f [ i − 2 ] ) + f [ i − 1 ] = ( i − 2 ) ∗ f [ i ] + f [ i − 1 ] (i-2)*(f[i-1]+f[i-2])+f[i-1]=(i-2)*f[i]+f[i-1] (i2)(f[i1]+f[i2])+f[i1]=(i2)f[i]+f[i1]
我们要得到 i ∗ f [ i ] i*f[i] if[i],是不是就要减去 f [ i − 1 ] f[i-1] f[i1]再加上 2 ∗ f [ i ] 2*f[i] 2f[i],所以,递推式就很清楚了 q [ i ] = q [ i − 1 ] + q [ i − 2 ] − f [ i − 1 ] + 2 ∗ f [ i ] q[i]=q[i-1]+q[i-2]-f[i-1]+2*f[i] q[i]=q[i1]+q[i2]f[i1]+2f[i]
所以原矩阵中我们需要 f [ i ] 和 f [ i − 1 ] f[i]和f[i-1] f[i]f[i1],有了它们俩,我们要递推出 q [ i ] q[i] q[i],是不是还要 q [ i − 2 ] 和 q [ i − 1 ] q[i-2]和q[i-1] q[i2]q[i1],而 T [ i − 1 ] = T [ i − 2 ] + q [ i − 1 ] T[i-1]=T[i-2]+q[i-1] T[i1]=T[i2]+q[i1],所以原矩阵就为
[ T n − 2 , q n − 2 , q n − 1 , f n − 1 , f n ] \begin{bmatrix}T_{n-2},q_{n-2},q_{n-1},f_{n-1},f_{n} \end{bmatrix} [Tn2qn2qn1fn1fn]
而我们要乘的矩阵就为
[ 1 , 0 , 0 , 0 , 0 0 , 0 , 1 , 0 , 0 1 , 1 , 1 , 0 , 0 0 , 0 , − 1 , 0 , 1 0 , 0 , 2 , 1 , 1 ] \begin{bmatrix} 1,0,0,0,0\\ 0,0,1,0,0\\ 1,1,1,0,0\\ 0,0,-1,0,1\\ 0,0,2,1,1\\ \end{bmatrix} 1,0,0,0,00,0,1,0,01,1,1,0,00,0,1,0,10,0,2,1,1
快速幂一下就行了

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long

int n, m;

struct Mx{
    int x, y;
    LL a[9][9];

    void cl(){
        memset(a, 0, sizeof(a));
        return ;
    }

    Mx operator * (const Mx &r)const{
        Mx p;
        for(int i = 1; i <= x; i ++){
            for(int j = 1; j <= r.y; j ++){
                p.a[i][j] = 0;
                for(int k = 1; k <= y; k++)
                    p.a[i][j]=(p.a[i][j]+a[i][k]*r.a[k][j]%m)%m;
            }
        }
        p.x = x, p.y = r.y;
        return p;
    }
}A, F;

Mx qkp(Mx b, int y){
    Mx sum;
    sum.cl();
    sum.x = sum.y = b.y;
    for(int i = 1; i < 6; i ++)
        sum.a[i][i] = 1;
    while( y ){
        if( y&1 )
            sum = sum * b;
        b = b * b;
        y >>= 1;
    }
    return sum;
}

int main(){
    scanf("%d%d", &n, &m);
    F.x = 1, F.y = 5;
    F.a[1][1] = 1, F.a[1][2] = 1;
    F.a[1][3] = 2, F.a[1][4] = 1;
    F.a[1][5] = 2;
    A.cl();
    A.x = A.y = 5;
    A.a[1][1] = A.a[2][3] = A.a[3][1] = A.a[3][2] = A.a[3][3] = 1;
    A.a[4][3] = -1;
    A.a[5][3] = 2;
    A.a[4][5] = A.a[5][4] = A.a[5][5] = 1;
    A = qkp(A, n-1);
    F = F * A;
    printf("%lld\n", F.a[1][1]%m);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值