到现在,斐波那契数列对于OIer们可谓无人不知无人不晓了
可是,对于大多数初初学者来说,他们只知道一个简单的递推式
而对于一些初学者来说(包括我在内),也只懂得一个矩阵快速幂去优化
事实上,还有一个 O ( 1 ) O(1) O(1) 时间复杂度的做法:黄金比例(虽然我看不懂证明过程)
那么我们就来讲讲斐波那契数列吧
斐波那契数列
举个例子,一个很普通的递推:这里有 n n n 层楼梯,每次你可以向上爬1层或2层,你现在在第1层楼梯,问你爬到第 n n n 层楼梯有多少方案。
很显然,递推式就是 F n = F n − 1 + F n − 2 F_n=F_{n-1}+F_{n-2} Fn=Fn−1+Fn−2(不用我解释了吧)
特别地 F 1 = 1 , F 2 = 1 F_1=1,F_2=1 F1=1,F2=1
其实我们可以再来看看他的式子 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89... 1,1,2,3,5,8,13,21,34,55,89... 1,1,2,3,5,8,13,21,34,55,89...,我们后面会讲到它。
这,就是斐波那契数列。
事实上,自然界万物都跟斐波那契数列息息相关,具体的例子我就不举了,你们自己上网白度去。
并且,斐波那契数列的应用太多太多了,同样的,你们上网百度去。
斐波那契数列——优化
但这种递推式虽然是 O ( n ) O(n) O(n) 的做法,但是当 n > 1 0 6 n>10^6 n>106 的时候,它就变得越来越力不从心了
那么有什么办法吗?
最常用也是最好理解的一种优化(虽然据我所知只有两种)是矩阵快速幂优化。
假设我们先建立一个矩阵 ( F 1 F 2 0 0 ) (\begin{matrix}F_1 & F_2 \\ 0 & 0 \end{matrix}) (F10F20) 我们考虑如何转移到 ( F n F n + 1 0 0 ) (\begin{matrix}F_n & F_n+1 \\ 0 & 0 \end{matrix}) (Fn0Fn+10) 。
先思考一个子问题:
( F 1 F 2 0 0 ) (\begin{matrix}F_1 & F_2 \\ 0 & 0 \end{matrix}) (F10F20) 如何转移到 ( F 2 F 3 0 0 ) (\begin{matrix}F_2 & F_3 \\ 0 & 0 \end{matrix}) (F20F30) ?
其实我们可以建立一个矩阵,使得它们两个乘起来可以转移到下一个矩阵
由于 F 3 = F 1 + F 2 F_3=F_1+F_2 F3=F1+F2 而且第二个矩阵里的 F 2 F_2 F2 可以通过原来的矩阵里的 F 2 F_2 F2 复制得来
如此我们便可以建立一个矩阵,设为 A A A, A = ( 0 1 1 1 ) A=(\begin{matrix}0 & 1 \\ 1 & 1\end{matrix}) A=(0111)。
把原始矩阵设为 S S S 吧。
那么我们转移只需要用 S S S 乘若干个 A A A 得来
由于矩阵乘法符合乘法结合律,所以这若干个 A A A 我们通过快速幂的方式实现。
答案即 S × A n S \times A^n S×An 的 ( 1 , 1 ) (1,1) (1,1)(1行1列)这个位置
#include<cstdio>
#include<cstring>
#define ll long long
#define R register
using namespace std;
const ll mod=1e8+7;
ll n,b;
ll A[5][5],F[5];
void mul(ll F[5],ll A[5][5]){
ll T[5];
memset(T,0,sizeof(T));
for (R int i=1;i<=2;i++)
for (R int j=1;j<=2;j++)
T[i]=(T[i]+F[j]*A[j][i]%mod)%mod;
for (R int i=1