题意
求斐波那契数列的第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<bits/stdc++.h>
#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;
}