矩阵连乘求超大斐波那契数 - ZZULI 1478 不死兔子

 

  矩阵乘法:
  [F(n+1)]=[1    1][F(n)   ]
  [F(n)    ]=[1    0][F(n-1)]
  结果
  F(n+1)=F(n)+F(n-1)
  F(n)=F(n)

  设[ 1    1]
     [ 1    0]为A

  则: F = A^n    所以Fib(n) = F[0][0]

  之前不知道斐波那契数的矩阵求法,了解了以后觉得矩阵的时间复杂度和迭代应该一样啊。其实,矩阵连乘的真正威力在于求高效幂的算法。

乘法方法有比加法更好的性质,用加法只能利用前两个的值,而乘法却不同,因为乘法有结合律,可以大幅度下降算法的耗时。

因为方阵的乘法有结合律,所以A(2)^n=A(2)^(n/2)*A(2)^(n/2),不妨设n是偶数所以求A(n)就可以化成求A(n/2)并作一次乘法。

所以递归方程是:T(n)=T(n/2)+O(1),它的解是O(logn)。

  代码没有优化,内存占用大概在 260K 左右。

不死兔子

Time Limit:1000MS  Memory Limit:65536K
Total Submit:278 Accepted:31

Description

一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔都不死,那么一年以后可以繁殖多少对兔子? 
我们不妨拿新出生的一对小兔子分析一下: 
  第一个月小兔子没有繁殖能力,所以还是一对; 
  两个月后,生下一对小兔,共有两对; 
  三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对; 
  ------ 
  依次类推可以列出下表: 



表中数字0,1,1,2,3,5,8---构成了一个数列,这个数列你是不是很熟悉呢? 
对于给定的非负整数 n ,请编写程序计算出经过n个月后的兔子对数,因为结果可能很大,请输出MOD (2011403)后的结果。 

Input

第1行是一个整数t, 
第2~t+1行,每行一个非负整数n(0≤n≤1 000 000 000) 

Output

对于每个n,请输出n个月后兔子的对数Mod(2011403)后的结果,每个结果占一行。

Sample Input

3
1
3
5

Sample Output

1
3
8

Hint

为解答此题,你可能需要更高效的算法

 代码:

#include <iostream>
#define M 2011403
using namespace std;
typedef __int64 LL;
class matrix{
public:
int mat[2][2];
matrix identity_matrix(){
//单位矩阵
matrix ret;
ret.mat[
0][0] = 1;
ret.mat[
1][1] = 1;
ret.mat[
0][1] = 0;
ret.mat[
1][0] = 0;
return ret;
}
matrix
operator * (matrix m){
matrix ret;
int i,j,k;
for(i = 0; i < 2; ++ i){
for(j = 0; j < 2; ++ j){
LL c
= 0;
for(k = 0; k < 2; ++ k)
c
= (c + (LL)mat[i][k] * m.mat[k][j] % M) % M;
ret.mat[i][j]
= c;
}
}
return ret;
}
matrix
operator ^ (int k){//高效幂
if(k == 0) return identity_matrix();
matrix ret
= * this, a = ret;
--k;
while(k){
if(k & 1) ret = ret * a;
a
= a * a;
k
>>= 1;
}
return ret;
}
};

int Fib(int n){
if(n <= 1)
return 1 % M;
matrix F;
F.mat[
0][0] = 1;
F.mat[
0][1] = 1;
F.mat[
1][0] = 1;
F.mat[
1][1] = 0;
F
= F ^ (n - 1);
return (F.mat[0][0] + F.mat[0][1] ) % M;
}

int main() {
int n,N;
cin
>>N;
while (N--){
cin
>>n;
cout
<<Fib(n)<<endl;
}
return 0;
}

另:刚开始做的时候用的是循环节,虽然AC可是内存占用巨大,差一点就超了,时间消耗也够呛。

代码:

//zzuli 1478
//此题是利用循环节做的
#include <iostream>
#define MOD 2011403
#define MAX 10000000
using namespace std;
int fib[MAX];
int main()
{
int T,n,i;
cin
>>T;
while(T--)
{
cin
>>n;
fib[
0]=fib[1]=1;
for(i=2;i<MAX;i++)
{
fib[i]
=(fib[i-1]+fib[i-2])%MOD;
if(fib[i]==1 && fib[i-1]==1)
break;
}
n
%=(i-1);
if(n==0)
cout
<<fib[i-1]<<endl;
else
cout
<<fib[n]<<endl;
}
return 0;
}
posted @ 2011-04-20 20:49  wwwwwwwww11we  阅读(333)  评论(2编辑  收藏  举报