pku 3070

pku 3070
pku 3070
题目要求计算Fibonacci数列的第n项最后4位。因为n可以很大(0 ≤ n ≤ 1,000,000,000)。因此直接计算在时限内是不可能的(有多个case)。题目还给出了计算的方法:表示成矩阵连乘的形式为



这就给我们提供了快速算法,因为矩阵相乘满足结合律。预先计算上面一个矩阵的2的幂次方,再将n表示成2进制。如当n=5时,只需计算一次矩阵乘法:1次方乘以4次方。当n=1000000000时最多只需计算29次矩阵乘法2^29 = 536870912),而且由于矩阵只是2阶,计算量大大减少。
//  pku 3070 求fabonacci数列的快速算法
/*

    化为以下矩阵连乘的形式
    |F(n+1) F(n)  | |1 1|(n)
    |F(n)   F(n-1)|=|1 0| 
    将n表示成2进制, 矩阵的二进制的积可以预先保存
    只需计算结果的后4位
*/
#include 
< iostream >

using namespace std;

int  m[ 31 ][ 4 ];  //  保存29个2阶矩阵
long  fact[ 31 ]; //  2的k次方

void  cal()
{
    
//  单位矩阵
    
// m[0][0]=1,m[0][1]=0,m[0][2]=0,m[0][3]=1;
    m[ 1 ][ 0 ] = 1 ,m[ 1 ][ 1 ] = 1 ,m[ 1 ][ 2 ] = 1 ,m[ 1 ][ 3 ] = 0 ;
    
// fact[0]=1;
    fact[ 1 ] = 1 ;
    
for ( int  i = 2 ;i <= 30 ;i ++ )
    {
        m[i][
0 ] = (m[i - 1 ][ 0 ] * m[i - 1 ][ 0 ] + m[i - 1 ][ 1 ] * m[i - 1 ][ 2 ]) % 10000 ;
        m[i][
1 ] = (m[i - 1 ][ 0 ] * m[i - 1 ][ 1 ] + m[i - 1 ][ 1 ] * m[i - 1 ][ 3 ]) % 10000 ;
        m[i][
2 ] = (m[i - 1 ][ 2 ] * m[i - 1 ][ 0 ] + m[i - 1 ][ 3 ] * m[i - 1 ][ 2 ]) % 10000 ;
        m[i][
3 ] = (m[i - 1 ][ 2 ] * m[i - 1 ][ 1 ] + m[i - 1 ][ 3 ] * m[i - 1 ][ 3 ]) % 10000 ;
        fact[i]
= 2 * fact[i - 1 ];
    }
}

void  solve( long  n)
{
    
//  对n表示成2进制
     int  e[ 31 ];
    memset(e,
0 ,sizeof(e));
    
int  i,j;
    
for (i = 30 ;i >= 1 ;i -- )
    {
        
if (n >= fact[i])
        {
            e[i]
= 1 ;
            n
-= fact[i];
        }
    }
    
// for(i=1;i<=30;i++) printf("%d\n",e[i]);
    
//   结果矩阵,初始时为单位矩阵
     int  res[ 4 ] = { 1 , 0 , 0 , 1 },tmp[ 4 ];
    
for (i = 1 ;i <= 30 ;i ++ )
    {
        
if (e[i] != 0 )
        {
            tmp[
0 ] = (res[ 0 ] * m[i][ 0 ] + res[ 1 ] * m[i][ 2 ]) % 10000 ;
            tmp[
1 ] = (res[ 0 ] * m[i][ 1 ] + res[ 1 ] * m[i][ 3 ]) % 10000 ;
            tmp[
2 ] = (res[ 2 ] * m[i][ 0 ] + res[ 3 ] * m[i][ 2 ]) % 10000 ;
            tmp[
3 ] = (res[ 2 ] * m[i][ 1 ] + res[ 3 ] * m[i][ 3 ]) % 10000 ;
            
for (j = 0 ;j < 4 ;j ++ ) res[j] = tmp[j];
        }
    }
    printf(
" %d\n " ,res[ 1 ]);
}

int  main()
{
    cal();
    
long  n;
    
while (scanf( " %ld " , & n)  &&  n >= 0 )
    {
        solve(n);
    }
    
return   1 ;
}

你可能感兴趣的:(pku 3070)