因为对矩阵加速非常不熟悉,所以菜鸡试着做了一下这道题
发现递推式和斐波拉契数列的递推式很像,也就是说应该可以用同样的手法做出来。
那就开始推式子吧:
[axax−1ax−2]=[ax−1+ax−3ax−1ax−2]=[ax−1ax−2ax−3]×[110001100]=⋯=[a3a2a1]×[110001100]x−3=[111]×[110001100]x−3\begin{aligned} \begin{matrix}[a_x&a_{x-1}&a_{x-2}]\end{matrix} &= \begin{bmatrix} a_{x-1}+a_{x-3} & a_{x-1} &a_{x-2} \end{bmatrix} \\ &= \begin{bmatrix} a_{x-1} & a_{x-2} & a_{x-3}\end{bmatrix} \times\begin{bmatrix} 1 & 1 & 0 \\ 0 & 0 &1 \\ 1 & 0 & 0 \end{bmatrix} \\ &= \cdots \\ &= \begin{bmatrix} a_3 & a_2 & a_1\end{bmatrix} \times \begin{bmatrix} 1 & 1 & 0 \\ 0 & 0 &1 \\ 1 & 0 & 0 \end{bmatrix}^{x-3} \\ &= \begin{bmatrix} 1 & 1 & 1 \end{bmatrix} \times \begin{bmatrix} 1 & 1 & 0 \\ 0 & 0 & 1 \\ 1 & 0 & 0 \end{bmatrix}^{x-3} \end{aligned}[axax−1ax−2]=[ax−1+ax−3ax−1ax−2]=[ax−1ax−2ax−3]×⎣⎡101100010⎦⎤=⋯=[a3a2a1]×⎣⎡101100010⎦⎤x−3=[111]×⎣⎡101100010⎦⎤x−3
式子推出来,题目就好办了,矩阵快速幂乱搞一下就AC了
代码:
在 2019/9/28\rm 2019/9/282019/9/28 重写了一份代码,比之前的那份不知道高到哪里去了好多了。
#include<cstdio>
#include<cstring>
#define rep(i,l,r) for (int i=l; i<=r; ++i)
const int P=1e9+7; int A[3][3];
struct node { int c[3][3]; };
const node F={ { {1,1,0},{0,0,1},{1,0,0} } }; //递推矩阵
const node E={ { {1,0,0},{0,1,0},{0,0,1} } }; //单位矩阵E
void operator *= (node &a,node b) { //这样重载常数要小一点
node t=a; memset(a.c,0,sizeof a.c);
rep(i,0,2) rep(j,0,2) rep(k,0,2)
(a.c[i][j]+=1ll*t.c[i][k]*b.c[k][j]%P)%=P;
}
node _power(node x,int y) { //快速幂
node ans=E;
while (y) {
if (y&1) ans*=x;
x*=x; y>>=1;
}
return ans;
}
int main() {
int T,n; scanf("%d",&T);
while (T--) {
scanf("%d",&n); if (n<4) { puts("1"); continue; }
memcpy(A,_power(F,n-3).c,sizeof A);
printf("%d\n",(1ll*A[0][0]+A[1][0]+A[2][0])%P);
//可以手算一下最后一步的矩阵乘法,答案就是这个式子
}
return 0;
}
简单地说一下,代码中的 EEE 是一个基础但重要的矩阵,它的左上-右下
对角线上的数字是1,其余的都是0。
它的重要特点在于:对于任意的矩阵 AAA ,都有 E×A=AE\times A=AE×A=A。它的意义其实和实数中的1是一样的。
所以在矩阵快速幂函数中,ansansans 初值赋为 EEE 这一步的意义其实相当于普通快速幂中的 ans=1ans=1ans=1 。