斐波拉契数列与矩阵快速幂

斐波拉契数列

斐波拉契数列又称为“兔子数列”,是一些列这样的数形成的数列:1、1、2、3、5、8、13、21、34、……其递推规律如下:

{ F [ n ] = 1   ( n < = 2 ) F [ n ] = F [ n − 1 ] + F [ n − 2 ]   ( n > 2 ) \begin{cases} F[n]=1 \ (n<=2)\\ F[n]=F[n-1]+F[n-2] \ (n>2) \\ \end{cases} {F[n]=1 (n<=2)F[n]=F[n1]+F[n2] (n>2)

由以上的递推关系,我们很快就能求得第N项斐波拉契数:

int fib(int n){
	if(n<=2) return 1;
	else return fib(n-1)+fib(n-2);
}

但由于递归频繁调用栈,并且会多次重复计算,我们可以用递推方式来求:

int a=1,b=1,c=1;
for(int i=3;i<=n;i++){
	c=a+b;
	b=a;
	a=c;
}

//数组存储
int a[maxn];
a[1]=1,a[2]=1;
for(int i=3;i<=n;i++){
	a[i]=a[i-1]+a[i-2];
}

这两种斐波拉契数列的时间复杂度都是O(n),对于一些较小的数比较实用,但对于特别大的数,这种方法就会超时。


对于较大的数(>1e9)我们可以使用矩阵快速幂来求解。
图片
(图片来源:https://blog.csdn.net/yhl1999/article/details/80994267

了解了这些,我们来看一道题:传送门

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<list>
#include<cmath>
#include<algorithm>
#define lson node<<1,st,mid
#define rson node<<1|1,mid+1,ed
#define mem(a,x) memset(a,x,sizeof(a))
#define me(a) memset(a,0,sizeof(a))
#define IOS ios::sync_with_stdio(false)
#define lowbit(x) x&(-x)
#define up(i,x,y) for(long long i=x;i<y;i++)
#define down(i,x,y) for(long long i=x;i>=y;i--)
#define in freopen("in.txt","r",stdin)
#define out freopen("out.txt","w",stdout)  	
typedef long long ll;
const ll mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f;
const int maxn = 1000005;
const double pi = acos(-1.0);
using namespace std;
ll qpow(ll a, ll b) { ll s = 1; while (b > 0) { if (b & 1)s = s * a % mod; a = a * a % mod; b >>= 1; }return s; }
ll read()     //输入外挂
{
    ll res = 0, ch, flag = 0;
    if ((ch = getchar()) == '-')
        flag = 1;
    else if (ch >= '0' && ch <= '9')
        res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
        res = res * 10 + ch - '0';
    return flag ? -res : res;
}
const ll N = 10;
struct Mat {
    ll mat[N][N];
};
Mat operator *(Mat a, Mat b) {
    Mat res;
    me(res.mat);
    for (ll i = 1; i <= 2; i++) {
        for (ll j = 1; j <= 2; j++) {
            for (ll k = 1; k <= 2; k++) {
                res.mat[i][j] = (res.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % mod;
            }
        }
    }
    return res;
}
ll quick_pow(Mat a, ll b) {
    Mat res;
    me(res.mat);
    for (ll i = 1; i <= N; i++) res.mat[i][i] = 1;
    while (b) {
        if (b & 1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res.mat[1][2];
}
int  main() {
    ll n;
    cin >> n;
    Mat a;
    a.mat[1][1] = a.mat[1][2] = a.mat[2][1] = 1;
    a.mat[2][2] = 0;
    ll x = quick_pow(a, n);
    ll y = quick_pow(a, n+1);
    cout << (x * y) % mod << endl;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值