题目
题目背景
大家都知道,斐波那契数列是满足如下性质的一个数列:
•
f
(
1
)
=
1
f(1) = 1
f(1)=1
•
f
(
2
)
=
1
f(2) = 1
f(2)=1
•
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(n) = f(n-1) + f(n-2)
f(n)=f(n−1)+f(n−2) (
n
≥
2
n ≥ 2
n≥2 且
n
n
n 为整数)
题目描述
请你求出
f
(
n
)
m
o
d
  
1000000007
f(n) \mod 1000000007
f(n)mod1000000007 的值。
输入输出格式
输入格式:
第1行:一个整数
n
n
n
输出格式:
第1行:
f
(
n
)
m
o
d
  
1000000007
f(n) \mod 1000000007
f(n)mod1000000007 的值
题解
- 前置技能:矩阵乘法
- 关于矩阵乘法:定义
A
n
m
,
B
m
p
A_{nm},B_{mp}
Anm,Bmp
A n m ∗ B m p = C n p A_{nm}*B_{mp}=C_{np} Anm∗Bmp=Cnp - 也就是说这有第一个矩阵的列数等于第二个矩阵的行数时才能相乘
C i , j = ∑ k = 1 m A i , k ∗ B k , j C_{i,j}=\sum_{k=1}^mA_{i,k}*B_{k,j} Ci,j=k=1∑mAi,k∗Bk,j - C i , j C_{i,j} Ci,j等于 A A A矩阵第 i i i行与 B B B矩阵第 j j j列分别相乘再相加
- 首先我们显然能在 O ( n ) O(n) O(n)的时间内求出斐波那契数列的第 n n n项,然而对于这道题我们需要优化一下
- 设
F = [ f n − 1 f n − 2 ] F ′ = [ f n f n − 1 ] F=\begin{bmatrix}f_{n-1}&f_{n-2}\end{bmatrix} \ \ \ \ F'=\begin{bmatrix}f_n&f_{n-1}\end{bmatrix} F=[fn−1fn−2] F′=[fnfn−1] - 若
F ′ = F ∗ A F'=F*A F′=F∗A - 则
A = [ 1 1 1 0 ] A=\begin{bmatrix}1&1\\1 &0\end{bmatrix} A=[1110] - 令
F 0 = [ f 2 f 1 ] = [ 1 1 ] F_0=\begin{bmatrix}f_2&f_1\end{bmatrix}=\begin{bmatrix}1&1\end{bmatrix} F0=[f2f1]=[11] - 则
F i = A i − 2 ∗ F 0 = [ f i f i − 1 ] F_i=A^{i-2}*F_0=\begin{bmatrix}f_{i}&f_{i-1}\end{bmatrix} Fi=Ai−2∗F0=[fifi−1] - 然后我们就可以愉快的用矩阵乘法和矩阵快速幂在 O ( 2 3 l o g T ) O(2^3logT) O(23logT)( T T T为递推次数)的复杂度下过掉本题
- p s : ps: ps:矩阵快速幂的原理和普通的快速幂原理相同快速幂模板
c o d e code code
#include <bits/stdc++.h>
using namespace std;
// const int maxn = 100000000 + 100;
#define mod 1000000007
typedef long long LL;
template <typename T>
inline void read(T &s) {
s = 0;
T w = 1, ch = getchar();
while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
s *= w;
}
LL k;
void mul(LL f[2], LL a[2][2]) {
LL c[2];
memset(c, 0, sizeof(c));
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k)
c[j] = (c[j] + (LL)f[k] * a[k][j]) % mod;
memcpy(f, c, sizeof(c));
}
void mul_self(LL a[2][2]) {
LL c[2][2];
memset(c, 0, sizeof(c));
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j)
for (int k = 0; k < 2; ++k)
c[i][j] = (c[i][j] + (LL)a[i][k] * a[k][j]) % mod;
memcpy(a, c, sizeof(c));
}
int main() {
read(k);
if (k <= 2) puts("1");
else {
LL f[2] = { 1, 1 };
LL a[2][2] = {{1, 1}, {1, 0}};
for (k = k - 2; k; k >>= 1) {
if (k & 1) mul(f, a);
mul_self(a);
}
printf("%lld\n", f[0]);
}
return 0;
}