前提

矩阵乘法板子

struct martix{
	ll m[10][10];
	void init(){clean(m,0);}
};
inline martix mutiply(martix input1,martix input2,int a,int b,int c){
	martix output;
	output.init();
	loop(i,1,a){
		loop(j,1,b){
			loop(k,1,c){
				output.m[i][k]+=input1.m[i][j]*input2.m[j][k];
				output.m[i][k]%=mod;
			}
		}
	}
	return output;
}

快速幂板子

inline martix fastpower(martix a,int times){
	martix stag=a;
	martix res=start_;//start_是单位矩阵,可以通过构造构造出此矩阵为对角线为1,其他都为0的矩阵
	while(times){
		if(times&1)res=mutiply(res,stag,2,2,2);
		times>>=1;
		stag=mutiply(stag,stag,2,2,2);
	}
	return res;
}

fibonacci 第n项

转化过程

  • StepOne:写出递推式:$f[i]=f[i-1]+f[i-2]$
  • StepTwo:考虑矩阵

首先,$f[i]$是由$f[i-1],f[i-2]$得出来的,所以当前矩阵应该需要$f[i],f[i-1],f[i-2]$

然后根据所有的已知条件写出如下草稿:

捕获.PNG

问号那一团就是我们要构造的转移矩阵

根据等量关系:

$$
f[n]=f[n-1]+f[n-2]\
$$

左边那一竖列问号应该是1

根据等量关系:

$$
f[n-1]=f[n-1]+0\times f[n-2]\
f[n-2]=f[n-2]+0\times f[n-1]
$$

右面一竖列问号号应该是1和0

最后构造出来的矩阵是:

捕获2.PNG

设构造出的转移矩阵为s,初始答案矩阵为a,,斐波那契数列的第n项为x,则

$$
a=\begin{pmatrix}
f[3] & f[2]\
f[2] &f[1]\
\end{pmatrix}
\s=
\begin{pmatrix}
1&1\1&0
\end{pmatrix}
\令矩阵k=a\times s^{n-3}
\x=k_{1\ 1}
$$

  • StepThree:可以搞了:
#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define ll long long
template<typename T>void read(T &x){
	x=0;char r=getchar();T neg=1;
	while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
	while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
	x*=neg;
}
ll n,mod;
const int maxn=2e9+10;
const int maxm=1e9+10+10;
struct martix{
	ll m[10][10];
	void init(){clean(m,0);}
}start_;
inline martix mutiply(martix input1,martix input2,int a,int b,int c){
	martix output;
	output.init();
	loop(i,1,a){
		loop(j,1,b){
			loop(k,1,c){
				output.m[i][k]+=input1.m[i][j]*input2.m[j][k];
				output.m[i][k]%=mod;
			}
		}
	}
	return output;
}
inline martix fastpower(martix a,int times){
	martix stag=a;
	martix res=start_;
	while(times){
		if(times&1)res=mutiply(res,stag,2,2,2);
		times>>=1;
		stag=mutiply(stag,stag,2,2,2);
	}
	return res;
}
int main(){
	#ifndef ONLINE_JUDGE
	//freopen("datain.txt","r",stdin);
	#endif
	read(n);
	read(mod);
	start_.init();
	start_.m[1][1]=start_.m[2][2]=1;
	martix ans;
	ans.init();
	ans.m[1][1]=2;ans.m[1][2]=1;ans.m[2][1]=1;ans.m[2][2]=1;
	if(n<=3){
		if(n==3)printf("2\n");
		else printf("1\n");
	}
	else{
		martix zhuanzhi;zhuanzhi.init();
		zhuanzhi.m[1][1]=1;zhuanzhi.m[1][2]=1;zhuanzhi.m[2][1]=1;zhuanzhi.m[2][2]=0;
		ans=mutiply(ans,fastpower(zhuanzhi,n-3),2,2,2);
		printf("%lld\n",ans.m[1][1]);
	}
	return 0;
}

一般线性递推的答案矩阵和转移矩阵构造

  • 观察递推式,看递推式中待求项和已知项之间的关系,如果待求项x要n个已知项yi来求的话,直接构造一个一行n列的答案矩阵,这样做的原因如下:

首先定义一个概念:层,对于一个线性递推等式,等式左边的项的层等于右边的项的层+1,如:$f[n]=f[n-1]+f[n-2]$,f[n]的层=f[n-1]的层+1/f[n-2]的层+1,那么显然有一个性质:
线性关系中,层数高的可以由层数低的”变换”而来,这个变换是指在低层项前乘上一个系数从而得到高层项

  • 根据矩阵乘法的概念推出转移矩阵(见上文)

一些练习:(写出答案矩阵和转移矩阵,答案不唯一)

  • $f[n]=f[n-1]+f[n-2]+n+1$

$$
\begin{pmatrix}
f[n-1] & f[n-2] & n & 1\
\end{pmatrix}
\times
\begin{pmatrix}
1 & 1 & 0 & 0\1 & 0 & 0 & 0\1 & 0 & 1 & 0\1 & 0 & 1 & 1
\end{pmatrix}
=\begin{pmatrix}
f[n] & f[n] & n+1 & 1\
\end{pmatrix}
$$

  • $f[n]=A\times f[n-1]+B\times f[n-2]$

$$
\begin{pmatrix}
f[n-1] & f[n-2]\
\end{pmatrix}
\times
\begin{pmatrix}
A & 1\
B & 0\
\end{pmatrix}
=\begin{pmatrix}
f[n] & f[n-1]\
\end{pmatrix}
$$