求斐波那契数列的第n项,但是这里的 0 ≤ n ≤ 1 0 9 0 \le n \le 10^9 0≤n≤109。
首先因为这儿的n很大,所以我们不能够直接像以前那样直接递推求出第n项,但是我们知道这道题,肯定是需要递推来求。所以这儿我们将引入矩阵快速幂的方法来进行求解。
我们都知道斐波那契数列的性质 f n = f n − 1 + f n − 2 , f n + 1 = f n + f n − 1 a n d n ≥ 3 f_n = f_{n-1}+f_{n-2},f_{n+1} = f_{n}+f_{n-1} \ \ and \ \ n \ge 3 fn=fn−1+fn−2,fn+1=fn+fn−1 and n≥3。那么到这儿我们如果能构造出一个矩阵A使得 F n ∗ A = F n + 1 F_n*A = F_{n+1} Fn∗A=Fn+1,那么如果我们得知矩阵 F 1 F_1 F1那么如果我们要求 F N F_N FN是不是只需要用 ( ( F 1 ∗ A ) ∗ A ) ∗ A . . . < 1 > ((F_1*A)*A)*A...<1> ((F1∗A)∗A)∗A...<1>这样一直乘下去乘上n-1次那么我们将能够求出 F n F_n Fn了,又因为对于矩阵乘法是具有结合律(这也是我们为什么能够进行快速幂的原因)那么我们就能够将<1>式转化成 F 1 ∗ A n − 1 F_1*A^{n-1} F1∗An−1而对于 A n − 1 A^{n-1} An−1如果我们能够快速求出,是不是就能够大大的节约时间了。那么对于以前来说我们快速求 a x a^x ax是不是用到了快速幂能够在 O ( l o g n ) O(log_n) O(logn)的时间求出。对于矩阵我们可以用同样的方法实现快速求出。
那么说了这么多,但是最关键的是我的 F 1 F_1 F1和 A A A这两个矩阵怎么去构造出来呢?这也是矩阵快速幂的难点。
对于本题来说我们可以根据斐波那契数列的性质去构造矩阵 F n F_n Fn
F n = [ f n − 2 , f n − 1 ] F_n= \begin{bmatrix} f_{n-2},f_{n-1} \end{bmatrix} Fn=[fn−2,fn−1]
因为 f n = f n − 1 + f n − 2 f_n = f_{n-1}+f_{n-2} fn=fn−1+fn−2
那么同理对于 F n + 1 F_{n+1} Fn+1
F n + 1 = [ f n − 1 , f n ] F_n+1= \begin{bmatrix} f_{n-1},f_{n} \end{bmatrix} Fn+1=[fn−1,fn]
那么前面我们说了 F n ∗ A = F n + 1 F_n*A=F_{n+1} Fn∗A=Fn+1那么根据矩阵乘法的性质矩阵A的大小是2*2。我们设矩阵A
A = [ a 1 a 3 a 2 a 4 ] A = \begin{bmatrix} a_1 & a_3\\ a_2& a_4 \end{bmatrix} A=[a1a2a3a4]
那么我们手算一下 F n ∗ A = F n + 1 F_n*A=F_{n+1} Fn∗A=Fn+1
那么就是
f n − 2 ∗ a 1 + f n − 1 ∗ a 2 = f n − 1 f n − 2 ∗ a 3 + f n − 1 ∗ a 4 = f n f_{n-2}*a_1+f_{n-1}*a_2= f_{n-1} \\ f_{n-2}*a_3+f_{n-1}*a_4= f_{n} fn−2∗a1+fn−1∗a2=fn−1fn−2∗a3+fn−1∗a4=fn
显然我们可以得知, a 1 = 0 , a 2 = 1 , a 3 = 1 , a 4 = 1 a_1 = 0, a_2 = 1, a_3 = 1, a_4 = 1 a1=0,a2=1,a3=1,a4=1,所以矩阵A
A = [ 0 1 1 1 ] A = \begin{bmatrix} 0 & 1\\ 1& 1 \end{bmatrix} A=[0111]
那么现在我们的A矩阵就求出来了。我们斐波那契数列的性质是当下标大于等于3的时候才能推出递推式。那么对于本题我们的 F 1 = [ f 1 , f 2 ] = [ 1 , 1 ] F_1 = [f1, f2] = [1, 1] F1=[f1,f2]=[1,1]我们应该是从第三项开始推起的,那么对于我上述的 A n − 1 A^{n-1} An−1这儿应该是 A n − 3 A^{n-3} An−3。
PS:对于矩阵快速幂的求法,大家可以参照龟速乘的做法
#include
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr)
#define int long long
#define endl "\n"
using namespace std;
const int N = 2, mod = 10000;
int n;
void mul(int c[], int b[], int a[][N])
{
int tmp[N] = {0};
for(int i = 0; i < N; i ++)
{
for(int j = 0; j < N; j ++)
{
tmp[i] = (tmp[i] + b[j] * a[j][i]) % mod;
}
}
memcpy(c, tmp, sizeof tmp);
}
void mul(int c[][N], int b[][N], int a[][N])
{
int tmp[N][N] = {0};
for(int i = 0; i < N ; i ++)
{
for(int j = 0; j < N; j ++)
{
for(int k = 0; k < N; k ++)
{
tmp[i][j] = (tmp[i][j] + b[i][k] * a[k][j]) % mod;
}
}
}
memcpy(c, tmp, sizeof tmp);
}
signed main()
{
while(cin >> n)
{
if(n == -1) break;
if(n == 0)
{
cout << 0 << endl;
continue;
}
if(n == 1 || n == 2)
{
cout << 1 << endl;
continue;
}
int f[N]={1, 1};
int a[N][N] = {
{0, 1},
{1, 1}
};
n -= 3;
while(n)
{
if(n & 1) mul(f, f, a);
mul(a, a, a);
n >>= 1;
}
cout << (f[0]+f[1]) % mod << endl;
}
return 0;
}