题目链接
题目概述
计算菲波那切数列,如果这个这个数的值不超过8位数,那么输出这个数,否则输出这个数的高42位和低4位,中间以...
分隔,输入以EOF
结束.
首先通过打表计算可以当(n < 40)时,这个数列的项是小于8位数的整数.低4位的计算可以通过计算时对10000
取模得到,但是因为菲波那切数列的递推式与前面两项相关,直接用递推式计算一个是可能会超时,因为(0 leq n leq 10^8),使用迭代计算时间复杂度为(O(n)),提交上去果然TLE
.可以把递推关系转为矩阵表示的:
[egin{aligned}
egin{bmatrix}
f(n) & f(n-1) \
0 & 0
end{bmatrix}
=
egin{bmatrix}
f(n-1) & f(n-2) \
0 & 0
end{bmatrix}
cdot
egin{bmatrix}
1 & 1 \
1 & 0
end{bmatrix}
end{aligned}
]
利用这种方式不断展开,可以得到
[egin{aligned}
egin{bmatrix}
f(n) & f(n-1) \
0 & 0
end{bmatrix}
=
egin{bmatrix}
f(1) & f(0) \
0 & 0
end{bmatrix}
cdot
egin{bmatrix}
1 & 1 \
1 & 0
end{bmatrix}^{n-1}
end{aligned}
]
定义了矩阵的乘法之后,矩阵的幂可以使用矩阵快速幂取模来计算.可以在(O(log(n)))的时间内计算出(f(n)\%10000).
高4位的计算要用到对数的性质,首先根据菲波那切数列数列的通项公式:
[f(n) = frac{1}{sqrt5}left((frac{1+sqrt 5}{2})^n-(frac{1-sqrt 5}{2})^n
ight)
]
在渐进意义下((n o infty))下(frac{1-sqrt 5}{2} < -1),从而((frac{1-sqrt 5}{2})^n) o 0)可以忽略不计.所以:
[x = log(frac{1}{sqrt5}(frac{1+sqrt 5}{2})^n)=log(frac{1}{sqrt5})+n*log(frac{1+lsqrt5}{2})
]
然后去掉(x)向下取整部分,在将这个数作为10的指数,得到一个介于(1到10)之间的整数,然后不断乘以10,就可以得到对应的高位的数字了.
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100;
ll F[N];
const int M = 2;
const int MOD = 10000;
struct Matrix{
ll m[M][M];
Matrix(){
memset(m, 0, sizeof(m));
}
};
void solve(){
F[1] = F[2] = 1;
for(int i = 3; i < N; i++){
F[i] = F[i - 1] + F[i - 2];
}
}
// 矩阵乘法
Matrix mul (const Matrix& a , const Matrix& b){
Matrix ans;
for (int i = 0; i < M; ++i){
for(int j = 0; j < M; ++j){
for (int k = 0; k < M; ++k){
ans.m[i][j] = (ans.m[i][j] + a.m[i][k] * b.m[k][j]) % MOD;
}
}
}
return ans;
}
// 矩阵快速幂取模
Matrix fast_exp_mod(Matrix a, int n){
Matrix ans;
for(int i = 0; i < M; i++){
ans.m[i][i] = 1;
}
while( n > 0){
if( n & 1 ){
ans = mul(ans,a);
}
n >>= 1;
a = mul( a, a );
}
return ans;
}
const double s = 1.0*(1 + sqrt(5.0))/2;
void solve(int n){
Matrix base;
base.m[0][0] = 1;
Matrix a;
a.m[0][0] = a.m[0][1] = a.m[1][0] = 1;
Matrix ans = fast_exp_mod(a, n-1);
ans = mul(base, ans);
double b = -0.5 * (log(5) / log(10)) + n*log(s) / log(10);
b -= floor(b);
double x = pow(10.0, b);
while( x < 1000){
x *= 10;
}
printf("%lld...%04lld
", (ll)x, ans.m[0][0]);
}
int main(int argc, const char** argv) {
solve();
int n= 0;
while(~scanf("%d", &n)){
if( n < 40){
printf("%lld
", F[n]);
}else{
solve(n);
}
}
return 0;
}