关于快速幂的讲解可以参见我的上一篇博客《快速幂》
题目链接:又见斐波那契
题目描述
这是一个加强版的斐波那契数列。
给定递推式
![](https://i-blog.csdnimg.cn/blog_migrate/c19a239f1bbebfa4defbfd9e4ddb91b6.png)
求F(n)的值,由于这个值可能太大,请对10
9+7取模。
输入描述:
第一行是一个整数T(1 ≤ T ≤ 1000),表示样例的个数。
以后每个样例一行,是一个整数n(1 ≤ n ≤ 1018)。
输出描述:
每个样例输出一行,一个整数,表示F(n) mod 1000000007。
示例1
输入
4 1 2 3 100
输出
1 16 57 558616258
这个相比普通的斐波那契数列多了后面四项,看一眼数据范围,使用普通的o(n)的算法肯定会超时,
因此这里需要使用矩阵快速幂(斐波那契数列的项数n一旦过大,就要考虑快速幂,普通算法时间空间都开销太大)。
使用矩阵快速幂的一个关键问题就是矩阵递推式。
参考普通快速幂那一片博客最后面的那个扩展式,就可以得到下面这个递推式了:
然后通过计算等价替换可得出该矩阵A:
下面只需要把普通斐波那契数列的构造由2*2的矩阵换为6*6的即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef long long ll;
// 用二维vector来表示矩阵
typedef vector<ll> vec;
typedef vector<vec> mat;
// 模
const int M = 1000000007;
// 计算 A*B
mat mul(mat& A, mat& B) {
mat C(A.size(), vec(B[0].size()));
for ( int i = 0 ; i < A.size() ; ++ i ) {
for ( int k = 0 ; k < B.size() ; ++ k ) {
for ( int j = 0 ; j < B[0].size() ; ++ j ) {
C[i][j] = (C[i][j]+A[i][k]*B[k][j]%M)%M;
}
}
}
return C;
}
// 计算 A^B
mat pow(mat A, ll n) {
mat B(A.size(), vec(A.size()));
for ( int i = 0 ; i < A.size() ; ++ i ) {
B[i][i] = 1;
}
while ( n > 0 ) {
if ( n & 1 ) B = mul(B, A);
A = mul(A, A);
n >>= 1;
}
return B;
}
int main()
{
int T;
scanf( "%d", &T );
ll n;
mat A(6, vec(6));
while ( T -- ) {
scanf( "%lld", &n );
if ( n == 0 ) { puts( "0" ); continue; }
if ( n == 1 ) { puts( "1" ); continue; }
for ( int i = 0 ; i < 6 ; ++ i ) {
for ( int j = 0 ; j < 6 ; ++ j ) {
A[i][j] = 0;
}
}
A[0][0] = 1; A[0][1] = 1; A[0][2] = 1; A[0][3] = 4; A[0][4] = 6; A[0][5] = 4;
A[1][0] = 1;
A[2][2] = 1; A[2][3] = 3; A[2][4] = 3; A[2][5] = 1;
A[3][3] = 1; A[3][4] = 2; A[3][5] = 1;
A[4][4] = 1; A[4][5] = 1;
A[5][5] = 1;
A = pow(A, n-1);
ll ans = 0;
for ( int i = 0 ; i < 6 ; ++ i ) {
if ( i == 1 ) continue;
ans = (ans+A[0][i])%M;
}
printf( "%lld\n", ans );
}
return 0;
}