杭电Fibonacci Numbers(难度极高,但是经典的矩阵快速幂问题的求解)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3117;

温馨提示:这道题有一定的数学难度和代码难度,不是非要搞懂这道题和彻底贯彻矩阵快速幂的应用劝退,不要和一个难题死磕;

题目思路:

这个题是很经典的,求解10^9个斐波那契数列;

但是这样肯定会溢出栈,10^9用long long 都显得有点苍白了更别说第10^9个斐波那契数列;

要知道,斐波那契数列的增长速度是极快的,用递推式根本无法完成;

所以说这个题将斐波那契数列转化成矩阵存储,然后计算矩阵的10^9幂;

这个题的思路可以转化成几个函数一起求解;

当然,矩阵快速幂我们已经了解过了:矩阵乘法+快速幂运算的结合体,

那这个题就是矩阵快速幂+斐波那契数列打表+数学转化+转制问题+......很多复杂的运算,所以说,这道题以向我这样的初级阶段acmer,理解思想和初步实现代码就ok了;

总之,这个题有难度,没事的时候可以当一个思维和数学的题来啃一啃,当然,还是离不开矩阵快速幂的运算:

Talk is cheap. Show me the code.

 1 #pragma GCC optimize(3)
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 typedef long long ll;
 5 const int N = 100;
 6 ll F[N];
 7 const int M = 2;
 8 const int MOD = 10000;
 9 struct Matrix{
10     ll m[M][M];
11     Matrix(){
12         memset(m, 0, sizeof(m));
13     }
14 };
15 void solve(){
16     F[1] = F[2] = 1;
17     for(int i = 3; i < N; i++){
18         F[i] = F[i - 1] + F[i - 2];
19     }
20 }
21 // 矩阵乘法
22 Matrix mul (const Matrix& a , const Matrix& b){
23     Matrix ans;
24     for (int i = 0; i < M; ++i){
25         for(int j = 0; j < M; ++j){
26             for (int k = 0; k < M; ++k){
27                 ans.m[i][j] = (ans.m[i][j] + a.m[i][k] * b.m[k][j]) % MOD;
28             }
29         }
30     }
31     return ans;
32 }
33 
34 // 矩阵快速幂取模
35 Matrix fast_exp_mod(Matrix a, int n){
36     Matrix ans;
37     for(int i = 0; i < M; i++){
38         ans.m[i][i] = 1;
39     }
40     while( n > 0){
41         if( n & 1 ){
42             ans = mul(ans,a);
43         }
44          a = mul( a, a );
45         n >>= 1;
46     }
47     return ans;
48 }
49 
50 const double s = 1.0*(1 + sqrt(5.0))/2;
51 
52 void deal(int n){
53     Matrix base;
54     base.m[0][0] = 1;
55     Matrix a;
56     a.m[0][0] = a.m[0][1] = a.m[1][0] = 1;
57     Matrix ans = fast_exp_mod(a, n-1);
58     ans = mul(base, ans);
59     double b = -0.5 * (log(5) / log(10)) + n*log(s) / log(10);
60     b -= floor(b);//取地板函数
61     double x = pow(10.0, b);
62     while( x < 1000){
63         x *= 10;
64     }
65     printf("%lld...%04lld\n", (ll)x, ans.m[0][0]);
66 }
67 
68 int main() {
69     std::ios::sync_with_stdio(false);
70     cin.tie(0);
71     cout.tie(0);
72     solve();//打表处理斐波那契数列
73     int n= 0;
74     while(~scanf("%d", &n)){
75         if( n < 40){
76             printf("%lld\n", F[n]);//小于第40个的斐波那契直接输出即可,能容下
77         }else{
78             deal(n);//其余情况另作处理
79         }
80     }
81     return 0;
82 }

 补充:与这道题相辅相成的是这道题http://poj.org/problem?id=3070

这道题中解释了普通一维递推式转置二维运算矩阵的大略;

 还有一道也是递推转置的题:

http://acm.hdu.edu.cn/showproblem.php?pid=6030

留着明天做吧

posted @ 2022-05-03 20:11  江上舟摇  阅读(31)  评论(0编辑  收藏  举报