Fibonacci Sequence 斐波那契数列
斐波那契数列十分的著名啊~~
递归式如下(前两项在一定情况下可能不同):
fn=⎧⎩⎨1,1,fn−1+fn−2,n = 1n = 2n ≥3
f
n
=
{
1
,
n = 1
1
,
n = 2
f
n
−
1
+
f
n
−
2
,
n ≥3
普通的算法复杂度是
O(n)
O
(
n
)
的在某些情况下可能会boom~~(比如说n取到
1022
10
22
),这种神题怎么做呢?当然是请出我们大名鼎鼎的矩阵啦。。
我们现有一个矩阵
A=[ab]
A
=
[
a
b
]
然后令
A×B=C
A
×
B
=
C
,其中C是一个2x2的矩阵,我们希望它是长成这样子的:
C=[bMcN]
C
=
[
b
c
M
N
]
,
M和N
M
和
N
是多少我们并不关心,我们要的是
c=a+b
c
=
a
+
b
,也就是Fibonacci中的下一个数,根据矩阵乘法的定义(不知道的话戳这)
C1,1=A1,1∗B1,1+A1,2∗B2,1=a∗B1,1+b∗B2,1=b
C
1
,
1
=
A
1
,
1
∗
B
1
,
1
+
A
1
,
2
∗
B
2
,
1
=
a
∗
B
1
,
1
+
b
∗
B
2
,
1
=
b
待定系数法得
B1,1=0,B2,1=1
B
1
,
1
=
0
,
B
2
,
1
=
1
同样的道理
C1,2=A1,1∗B1,2+A1,2∗B2,2=a∗B1,2+b∗B2,2=c=a+b
C
1
,
2
=
A
1
,
1
∗
B
1
,
2
+
A
1
,
2
∗
B
2
,
2
=
a
∗
B
1
,
2
+
b
∗
B
2
,
2
=
c
=
a
+
b
同样的待定系数法得
B1,2=1,B2,2=1
B
1
,
2
=
1
,
B
2
,
2
=
1
由此,我们得到的
B=[0111]
B
=
[
0
1
1
1
]
,这就是转移矩阵。
于是我们想要求得
fn
f
n
就是通过
A×Bn−1=C
A
×
B
n
−
1
=
C
得到的矩阵C中的第一行第一列元素,这样就很明显了,矩阵快速幂一上复杂度就降到
O(nlog2n)
O
(
n
l
o
g
2
n
)
了。可以解决问题。
Code:
#include<cstdio>
#include<cassert>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
typedef long long ll;
const int LIMIT = 1e9 + 7;
struct Matrix {
int row, col, limit;
std::vector< std::vector< ll > > num;
Matrix() {
row = col = 0;
limit = 0x3f3f3f3f;
num.clear();
}
Matrix(int _row, int _col, int _limit) {
this->row = _row;
this->col = _col;
this->limit = _limit;
num.resize(row + 1);
for (int i = 1; i <= row; i++)
num[i].resize(col + 1);
}
Matrix operator * (const Matrix& rhs) {
assert(this->col == rhs.row);
Matrix ans = Matrix(this->row, col, this->limit);
for (int i = 1; i <= this->row; i++)
for (int j = 1; j <= rhs.col; j++)
for (int k = 1; k <= this->col; k++)
ans.num[i][j] += this->num[i][k] * rhs.num[k][j],
ans.num[i][j] %= this->limit;
return ans;
}
Matrix operator ^ (ll p) {
assert(this->row == this->col);
Matrix ans = Matrix(this->row, this->col, this->limit);
for (int i = 1; i <= this->row; i++)
ans.num[i][i] = 1;
while (p) {
if (p & 1) ans = ans * *this;
*this = *this * *this;
p >>= 1;
}
return ans;
}
};
int main() {
long long n;
scanf("%lld", &n);
if (n == 1) {
puts("1");
return 0;
}
Matrix a = Matrix(1, 2, LIMIT);
Matrix b = Matrix(2, 2, LIMIT);
a.num[1][1] = a.num[1][2] = 1;
b.num[1][2] = b.num[2][1] = b.num[2][2] = 1;
b = b ^ (n - 1);
a = a * b;
printf("%lld\n", a.num[1][1]);
}