「POJ 3070」Fibonacci - 矩阵乘法

题意描述

POJ 链接

给定一个数 n,求斐波那契数列第 n 项的后四位。

数据范围:1n109

解题思路

我们可以使用矩阵来加速地推。

我们知道斐波那契的通项公式为:

ai=ai1+ai2

故我们可以设矩阵 F(n)

F(n)=[anan+1]

同时我们可以设:

F(n)=F(n1)×X

即为:

[anan+1]=[an1an]×X

我们可以推出:

[anan+1]=[an1an]×[0111]

所以我们最终可以推出下列式子:

F(n)=F(1)×[0111]n1=[11]×[0111]n1

最终利用快速幂就可以在 O(logn) 的时间复杂度中求出答案。

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <cstdio>
#include <cstring>
#include <algorithm>

int fib[1 + 1][2 + 1], x[2 + 1][2 + 1];

void init() {
fib[1][1] = fib[1][2] = 1;
x[1][1] = 0;
x[1][2] = x[2][1] = x[2][2] = 1;
}

void mul(int a[][2 + 1], int ax, int ay, int b[][2 + 1], int bx, int by, int ans[][2 + 1], int mod) {
int s1[2 + 1][2 + 1], s2[2 + 1][2 + 1];
memcpy(s1, a, sizeof(int) * (ax + 1) * (ay + 1));
memcpy(s2, b, sizeof(int) * (bx + 1) * (by + 1));

for (int i = 1; i <= ax; i++) {
for (int j = 1; j <= by; j++) {
int sum = 0;
for (int k = 1; k <= ay; k++) sum = (sum + s1[i][k] * s2[k][j]) % mod;
ans[i][j] = sum;
}
}
}

void pow(int a[][2 + 1], int ax, int ay, int p, int ans[][2 + 1], int mod) {
int s1[2 + 1][2 + 1];
memcpy(s1, a, sizeof(int) * (2 + 1) * (2 + 1));
memset(ans, 0, sizeof(int) * (2 + 1) * (2 + 1));
for (int i = 1; i <= std::min(ax, ay); i++) ans[i][i] = 1;


for (; p; p >>= 1) {
if (p & 1) mul(ans, 2, 2, s1, 2, 2, ans, mod);
mul(s1, 2, 2, s1, 2, 2, s1, mod);
}
}

int main() {
int n;

while (scanf("%d", &n) != EOF) {
if (n == -1) break;
if (n == 0) {
printf("0\n");
continue;
}
if (n == 1 || n == 2) {
printf("1\n");
continue;
}
if (n == 3) {
printf("2\n");
continue;
}

init();
pow(x, 2, 2, n - 1, x, 10000);
mul(fib, 1, 2, x, 2, 2, fib, 10000);

printf("%d\n", fib[1][1]);
}

return 0;
}