求斐波那契数列第n项大家肯定都会。但是如果让我们求斐波那契的第1e9项,我们还能一项一项递推吗?这显然会T到起飞。所以我们要想点办法来加速递推。那么办法就是用矩阵+快速幂。
这样一列,我们就得到了,和三者的关系。用矩阵表示一下就是
那么就有了,我们如果多左乘一个系数矩阵,那么就会从变成。
那么也就是,如果刚开始的一个矩阵,我们通过左乘n次,就能得到矩阵。那么只要求一下就得到了,其中n>=2。当n<2的时候就直接输出1。
求幂我们有快速幂,这样就能降到级别了。其中,我们只要把快速幂里的乘,改成我们手写的矩阵乘法就行了。
下面给出一个例题: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
题目描述
a[1]=a[2]=a[3]=1
a[x]=a[x-3]+a[x-1] (x>3)
求a数列的第n项对()取余的值。
输入输出格式
输入格式:
第一行一个整数T,表示询问个数。
以下T行,每行一个正整数n。
输出格式:
每行输出一个非负整数表示答案。
是个递推式,那么你很轻松就写出了递推方程:
我们又轻松找到了和的关系。
又是一波快速幂!我们甚至只要在上一题的模板改一下就得到双倍经验了!
#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%的数据满足 且.
你又是很轻松地写出来了递推公式,。
,其中k=,也就是说,我们的快速幂要分阶段来。这太烦了我就没写,留给你写叭!
那么写完了这题,我们再来一题叭!https://www.luogu.org/problemnew/show/P5175
题目描述
一个数列,已知及两项。
数列满足递推式
求
由于答案可能过大,对取模。
那就留给你做吧!完结撒花!