加强版斐波那契数列(矩阵快速幂)

关于快速幂的讲解可以参见我的上一篇博客《快速幂》

题目链接:又见斐波那契

题目描述 

这是一个加强版的斐波那契数列。
给定递推式
求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的即可。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <cstring>
  6. using namespace std;
  7. typedef long long ll;
  8. // 用二维vector来表示矩阵
  9. typedef vector<ll> vec;
  10. typedef vector<vec> mat;
  11. // 模
  12. const int M = 1000000007;
  13. // 计算 A*B
  14. mat mul(mat& A, mat& B) {
  15. mat C(A.size(), vec(B[0].size()));
  16. for ( int i = 0 ; i < A.size() ; ++ i ) {
  17. for ( int k = 0 ; k < B.size() ; ++ k ) {
  18. for ( int j = 0 ; j < B[ 0].size() ; ++ j ) {
  19. C[i][j] = (C[i][j]+A[i][k]*B[k][j]%M)%M;
  20. }
  21. }
  22. }
  23. return C;
  24. }
  25. // 计算 A^B
  26. mat pow(mat A, ll n) {
  27. mat B(A.size(), vec(A.size()));
  28. for ( int i = 0 ; i < A.size() ; ++ i ) {
  29. B[i][i] = 1;
  30. }
  31. while ( n > 0 ) {
  32. if ( n & 1 ) B = mul(B, A);
  33. A = mul(A, A);
  34. n >>= 1;
  35. }
  36. return B;
  37. }
  38. int main()
  39. {
  40. int T;
  41. scanf( "%d", &T );
  42. ll n;
  43. mat A(6, vec(6));
  44. while ( T -- ) {
  45. scanf( "%lld", &n );
  46. if ( n == 0 ) { puts( "0" ); continue; }
  47. if ( n == 1 ) { puts( "1" ); continue; }
  48. for ( int i = 0 ; i < 6 ; ++ i ) {
  49. for ( int j = 0 ; j < 6 ; ++ j ) {
  50. A[i][j] = 0;
  51. }
  52. }
  53. A[ 0][ 0] = 1; A[ 0][ 1] = 1; A[ 0][ 2] = 1; A[ 0][ 3] = 4; A[ 0][ 4] = 6; A[ 0][ 5] = 4;
  54. A[ 1][ 0] = 1;
  55. A[ 2][ 2] = 1; A[ 2][ 3] = 3; A[ 2][ 4] = 3; A[ 2][ 5] = 1;
  56. A[ 3][ 3] = 1; A[ 3][ 4] = 2; A[ 3][ 5] = 1;
  57. A[ 4][ 4] = 1; A[ 4][ 5] = 1;
  58. A[ 5][ 5] = 1;
  59. A = pow(A, n -1);
  60. ll ans = 0;
  61. for ( int i = 0 ; i < 6 ; ++ i ) {
  62. if ( i == 1 ) continue;
  63. ans = (ans+A[ 0][i])%M;
  64. }
  65. printf( "%lld\n", ans );
  66. }
  67. return 0;
  68. }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值