矩阵加速数列(学习笔记)

当我们遇到这样一类问题:已知递推式,但数据范围太大,直接递推下去肯定会超时,例如求斐波拉契数列的第n项(\(n<=10^{18}\))等等.这是我们就需要用到矩阵来加速递推,优化时间复杂度.

步骤和思想都很简单.以求斐波拉契数列的第n项为例.我们已知递推式\(f(n)=f(n-2)+f(n-1)\),把等式两边各抽象为一个矩阵,即我们已知\(S(n-1)=[f(n-2),f(n-1)]\),要求出\(S(n)=[f(n-1),f(n)]\),那么\(S(n-1)\)如何变换到\(S(n)\)呢?可以乘上状态矩阵\(T= [\begin{matrix} 0 & 1 \\1 & 1 \end{matrix}]\),这里可以自己根据矩阵乘法运算法则手玩出来.

于是就可以设出初始矩阵\(S(0)\),然后\(S(n)=S(0)*T^n\),显然\(T^n\)可以通过矩阵快速幂来求得.my题解

看几道模板题.

洛咕 my题解

洛咕

题意:\(a[1]=a[2]=a[3]=1\);\(a[x]=a[x-3]+a[x-1] (x>3)\).求a数列的第n项对1e9+7取余的值.

模板题,就直接放代码了.

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
const int mod=1e9+7;
struct matrix{
    int a[3][3];
    matrix operator *(matrix b){
    matrix c;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            c.a[i][j]=0;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            for(int k=0;k<3;k++)
                c.a[i][j]=(c.a[i][j]+1LL*a[i][k]*b.a[k][j])%mod;
    return c;
    }
}S,T;
int main(){
    int Q=read();
    while(Q--){
        S.a[0][0]=0;S.a[0][1]=1;S.a[0][2]=1;
        T.a[0][0]=0;T.a[0][1]=0;T.a[0][2]=1;
        T.a[1][0]=1;T.a[1][1]=0;T.a[1][2]=0;
        T.a[2][0]=0;T.a[2][1]=1;T.a[2][2]=1;
        int n=read();
        while(n){if(n&1)S=S*T;T=T*T;n>>=1;}
        printf("%d\n",S.a[0][0]);   
    }
    return 0;
}

洛咕

广义的斐波那契数列是指形如\(a_n=p\times a_{n-1}+q\times a_{n-2}\)的数列.给定数列的两系数\(p\)\(q\),以及数列的最前两项\(a_1\)\(a_2\),另给出两个整数\(n\)\(m\),试求数列的第\(n\)\(a_n\)除以\(m\)的余数.

分析:按照步骤列出已知矩阵\(S(n-1)=[f(n-1),f(n-2)]\),目标矩阵\(S(n)=[f(n),f(n-1)]\),手玩出转移矩阵\(T=[\begin{matrix} p & 1 \\q & 0 \end{matrix}]\).

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline LL read(){
    LL s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
LL p,q,a1,a2,n,m;
struct matrix{
    LL a[2][2];
    matrix operator *(matrix b){
    matrix c;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
            c.a[i][j]=0;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<2;k++)
                c.a[i][j]=(c.a[i][j]+1LL*a[i][k]*b.a[k][j])%m;
    return c;
    }
}S,T;
int main(){
    p=read();q=read();a1=read();a2=read();n=read();m=read();
    S.a[0][0]=a2;S.a[0][1]=a1;
    T.a[0][0]=p;T.a[0][1]=1;
    T.a[1][0]=q;T.a[1][1]=0;
    n-=2;
    while(n){if(n&1)S=S*T;T=T*T;n>>=1;}
    printf("%lld\n",S.a[0][0]%m);   
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/PPXppx/p/10587675.html
今日推荐