矩阵快速幂加速递推

求斐波那契数列第n项大家肯定都会。但是如果让我们求斐波那契的第1e9项,我们还能一项一项递推吗?这显然会T到起飞。所以我们要想点办法来加速递推。那么办法就是用矩阵+快速幂。

F_n_+_1=1*F_n+1*F_n_-_1

F_n=1*F_n+0*F_n_-_1

这样一列,我们就得到了F_n_+_1,F_n,和F_n,F_n_-_1三者的关系。用矩阵表示一下就是

\large \begin{pmatrix} &F_n_+_1 \\ &F_n \end{pmatrix} = \begin{pmatrix} 1 &1\\1&0\end{pmatrix}\times \begin{pmatrix} F_n\\F_n_-_1\end{pmatrix}

那么就有了,我们如果多左乘一个系数矩阵,那么A_{_{11}}就会从F_n变成F_n_+_1

\large \begin{pmatrix} &F_n_+_2 \\ &F_n_+_1 \end{pmatrix} = \begin{pmatrix} 1 &1\\1&0\end{pmatrix}\times \begin{pmatrix} F_n_+_1\\F_n\end{pmatrix} = \begin{pmatrix} 1 &1\\1&0\end{pmatrix}\times \begin{pmatrix} 1 &1\\1&0\end{pmatrix}\times \begin{pmatrix} F_n_+_1\\F_n\end{pmatrix}


那么也就是,如果刚开始的一个矩阵\large \binom{F_1}{F_1},我们通过左乘n次\large \begin{pmatrix} &1 &1 \\ &1 &0 \end{pmatrix},就能得到矩阵\large \binom{F_n_+_2}{F_n_+_1}。那么只要求一下\large \begin{pmatrix} &1&1 \\ & 1&0 \end{pmatrix}^{n-2}\times\begin{pmatrix} &1\\ &1 \end{pmatrix}就得到了\large \binom{F_n}{F_n_-_1},其中n>=2。当n<2的时候就直接输出1。

求幂我们有快速幂,这样就能降到\large \lg级别了。其中,我们只要把快速幂里的乘,改成我们手写的矩阵乘法就行了。

下面给出一个例题:https://www.luogu.org/problemnew/show/P1962

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<algorithm>
#include<cmath>
#define rg register
#define il inline
using namespace std;
typedef unsigned long long ll;
ll read(){
    ll ans=0,flag=1;char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1;
    ans=ch^48;
    while((ch=getchar())>='0'&&ch<='9') ans=(ans<<3)+(ans<<1)+(ch^48);
    return flag*ans;
}
void write(ll x){
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int mod=1000000007;
struct matrix{
    ll val[2][2];
}shit={{1,1,1,0}};
matrix mul(matrix a,matrix b){
    matrix ans;
    for(rg int i=0;i<2;i++){
        for(rg int j=0;j<2;j++){
            ans.val[i][j]=0;
            for(rg int k=0;k<2;k++)
                ans.val[i][j]=(ans.val[i][j]+a.val[i][k]*b.val[k][j])%mod;
        }
    }
    return ans;
}
matrix faspow(matrix a,ll b){
    matrix ans;
    for(rg int i=0;i<2;i++) for(rg int j=0;j<2;j++) if(i==j) ans.val[i][j]=1; else ans.val[i][j]=0;
    while(b){
        if(b&1) ans=mul(ans,a);
        b>>=1;
        a=mul(a,a);
    }
    return ans;
}
int main(){
    ll t=read();
    matrix ans;
        if(t<=2) cout<<"1\n";
        else{
            ans=faspow(shit,t-2);
            cout<<(ans.val[0][0]+ans.val[0][1])%mod<<endl;
        }
    return 0;
}

那么把这题做了,你大概就知道什么是矩阵快速幂了。那么再来一个题:https://www.luogu.org/problemnew/show/P1939

扫描二维码关注公众号,回复: 5580063 查看本文章

题目描述

a[1]=a[2]=a[3]=1

a[x]=a[x-3]+a[x-1] (x>3)

求a数列的第n项对\large 1000000007\large 10^9+7)取余的值。

输入输出格式

输入格式:

第一行一个整数T,表示询问个数。

以下T行,每行一个正整数n。

输出格式:

每行输出一个非负整数表示答案。

是个递推式,那么你很轻松就写出了递推方程:

\large a[n]=1*a[n-1]+0*a[n-2]+1*a[n-3]

\large a[n-1]=1*a[n-1]+0*a[n-2]+0*a[n-3]

\large a[n-2]=0*a[n-1]+1*a[n-2]+0*a[n-3]

我们又轻松找到了\large a_n,a_n_-_1,a_n_-_2\large a_n_-_1, a_n_-_2, a_n_-_3的关系。

\large \begin{pmatrix} &a_n\\ & a_n_-_1\\ & a_n_-_2& \end{pmatrix}= \begin{pmatrix} &1 &0 &1 \\ &1 &0 &0 \\ &0 &1 &0 \end{pmatrix}\times\begin{pmatrix} &a_n_-_1\\ &a_n_-_2\\ &a_n_-_3\\ \end{pmatrix}

又是一波快速幂!我们甚至只要在上一题的模板改一下就得到双倍经验了!

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<algorithm>
#include<cmath>
#define rg register
#define il inline
using namespace std;
typedef unsigned long long ll;
ll read(){
    ll ans=0,flag=1;char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1;
    ans=ch^48;
    while((ch=getchar())>='0'&&ch<='9') ans=(ans<<3)+(ans<<1)+(ch^48);
    return flag*ans;
}
void write(ll x){
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int mod=1000000007;
struct matrix{
    ll val[3][3];
}shit={{1,0,1,1,0,0,0,1,0}};
matrix mul(matrix a,matrix b){
    matrix ans;
    for(rg int i=0;i<3;i++){
        for(rg int j=0;j<3;j++){
            ans.val[i][j]=0;
            for(rg int k=0;k<3;k++)
                ans.val[i][j]=(ans.val[i][j]+a.val[i][k]*b.val[k][j])%mod;
        }
    }
    return ans;
}
matrix faspow(matrix a,int b){
    matrix ans;
    for(rg int i=0;i<3;i++) for(rg int j=0;j<3;j++) if(i==j) ans.val[i][j]=1; else ans.val[i][j]=0;
    while(b){
        if(b&1) ans=mul(ans,a);
        b>>=1;
        a=mul(a,a);
    }
    return ans;
}
int main(){
    matrix ans;
    int t,n=read();
    while(n--){
        t=read();
        if(t<=3) cout<<"1\n";
        else{
            ans=faspow(shit,t-3);
            cout<<(ans.val[0][0]+ans.val[0][1]+ans.val[0][2])%mod<<endl;
        }
    }
    return 0;
}

做完这两个题你肯定对矩阵快速幂有一点点了解了,那就再来一个题:https://www.luogu.org/problemnew/show/P3216

题目描述

小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:

给定正整数 N和M ,要求计算Concatenate(1..N) ModM 的值,其中 Concatenate (1 .. N)是将所有正整数 1, 2, …, N顺序连接起来得到的数。例如,N=13, Concatenate (1 .. N)=12345678910111213,小C 想了大半天终于意识到这是一道不可能手算出来的题目,于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。

输入输出格式

输入格式:

输入文件只有一行且为用空格隔开的两个正整数N和M,其中30%的数据满足1≤N≤1000000;100%的数据满足1\leq N\leq10^{18} 且1\leq M \leq 10^9.

你又是很轻松地写出来了递推公式,\large C[n]=C[n-1]*10^{\lceil \lo\log_{10}n \rceil+1}+n

\large \begin{pmatrix}f_n \\n \\1\end{pmatrix}=\begin{pmatrix}10^k & 1 & 1 \\0 & 1 & 1 \\0 & 0 & 1\end{pmatrix}\times\begin{pmatrix}f_{n-1} \\n-1 \\1\end{pmatrix}⎣,其中k=\large {\lceil \lo\log_{10}n \rceil+1,也就是说,我们的快速幂要分阶段来。这太烦了我就没写,留给你写叭!

那么写完了这题,我们再来一题叭!https://www.luogu.org/problemnew/show/P5175

题目描述

一个数列​a_n,已知a_1a_2​两项。

数列​a_n满足递推式\large a_n=x \times a_{n-1}+ y \times a_{n-2}(n\geq3).​

求 \large \sum_{i=1}^na_i^2

由于答案可能过大,对\large 10^{9}+7取模。

那就留给你做吧!完结撒花!

猜你喜欢

转载自blog.csdn.net/ffscas/article/details/87997733