在ACM中,许多复杂的递推关系式往往和斐波那契数列有着密切的关系,而一旦和斐波那契数列关系上了,你不用多想了,这题一定得用到矩阵&快速幂。因为斐波那契数列是没有通项公式的。

首先我们得先了解一般斐波那契数列的矩阵求法。斐波那契数列的定义是F(0)=0,F(1)=1之后的项就是前两项之和。经过计算可得

A={1 1       B={1 1      A*B的n次方即斐波那契数列的n+2项。即我们要求第n项,须得先求B方阵的n-2次。而这一步就需要用到快速幂,快速幂的效率是n(log

      0 0}           1 0}     (n)),n就可以很大了。

矩阵+快速幂的斐波那契数列代码:

#include<iostream>
#include<stdio.h>
using namespace std;
#define mdu 1000000007;
#define N 15
struct matrix
{
    __int64 a[N][N];
};
matrix chu,die;
matrix mul(matrix a1,matrix a2,int ii)
{
    int i,j,k;
    matrix c;
    for(i=1;i<=ii;i++)
      for(j=1;j<=ii;j++)
      {
             c.a[i][j]=0;
           for(k=1;k<=ii;k++)
             c.a[i][j]+=a1.a[i][k]*a2.a[k][j]%mdu;
           c.a[i][j]%=mdu;    
        }
   return c;
}
void qmi(__int64 nn)
{
    while(nn>0)
    {
        if(nn%2==1) chu=mul(chu,die,2);
        die=mul(die,die,2);
        nn=nn>>1;
    }
}
int main()
{
    __int64 n;
    
    while(scanf("%I64d",&n)!=EOF)
    {
        chu.a[1][1]=1; chu.a[1][2]=1;
        chu.a[2][1]=0; chu.a[2][2]=0;
        die.a[1][1]=1; die.a[1][2]=1;
        die.a[2][1]=1; die.a[2][2]=0;
        if(n!=1 || n!=2) qmi(n-2);
        printf("%The number is:%I64d\n",chu.a[1][1]);
    }
    

    return 0;
}
View Code

我们再来看几个典型例题:

M斐波那契数列

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 1050    Accepted Submission(s): 312


Problem Description
M斐波那契数列F[n]是一种整数数列,它的定义如下:

F[0] = a
F[1] = b
F[n] = F[n-1] * F[n-2] ( n > 1 )

现在给出a, b, n,你能求出F[n]的值吗?
 

 

Input
输入包含多组测试数据;
每组数据占一行,包含3个整数a, b, n( 0 <= a, b, n <= 10^9 )
 

 

Output
对每组测试数据请输出一个整数F[n],由于F[n]可能很大,你只需输出F[n]对1000000007取模后的值即可,每组数据输出一行。
 

 

Sample Input
0 1 0
6 10 2
Sample Output
0 60
这道题由加法改成了乘法,稍加思索发现其实B的次方的求法几乎不变,改变的只是在得到答案的时候所有乘法都改成次方即可。
话不多说,上代码:
#include<iostream>
#include<stdio.h>
using namespace std;
#define mdu 1000000007
#define N 15
struct matrix
{
    __int64 a[N][N];
};
matrix chu,die;
matrix mul(matrix a1,matrix a2,int ii)
{
    int i,j,k;
    matrix c;
    for(i=1;i<=ii;i++)
      for(j=1;j<=ii;j++)
      {
             c.a[i][j]=0;
           for(k=1;k<=ii;k++)
             c.a[i][j]=(c.a[i][j]+a1.a[i][k]*a2.a[k][j])%(mdu-1);    
        }
   return c;
}
__int64 kmi(__int64 a,__int64 x)
{
    __int64 temp=a;
    __int64 cnt=1;
    while(x>0)
    {
        if(x%2==1)  cnt=cnt*temp%mdu;
        temp=temp*temp%mdu;
        x=x>>1;
    }
    return cnt;
}
void qmi(__int64 nn)
{
    while(nn>0)
    {
        if(nn%2==1){
            matrix c;
            c.a[1][1]=kmi(chu.a[1][1],die.a[1][1])*kmi(chu.a[1][2],die.a[2][1])%mdu;
            c.a[1][2]=kmi(chu.a[1][1],die.a[1][2])*kmi(chu.a[1][2],die.a[2][2])%mdu;
            chu=c;
        }
        die=mul(die,die,2);
        nn=nn>>1;
    }
}
int main()
{
    __int64 n,a,b;      
    while(scanf("%I64d%I64d%I64d",&a,&b,&n)!=EOF)
    {
        chu.a[1][1]=b; chu.a[1][2]=a;
        chu.a[2][1]=0; chu.a[2][2]=0;
        die.a[1][1]=1; die.a[1][2]=1;
        die.a[2][1]=1; die.a[2][2]=0;
        if(n==0) { printf("%I64d\n",chu.a[1][2]); continue; }
        if(n==1) { printf("%I64d\n",chu.a[1][1]); continue; }
        qmi(n-1);
        printf("%I64d\n",chu.a[1][1]);
    }
    

    return 0;
}
View Code

我们再来看一题:

Lucky Coins Sequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 627    Accepted Submission(s): 329


Problem Description
As we all know,every coin has two sides,with one side facing up and another side facing down.Now,We consider two coins's state is same if they both facing up or down.If we have N coins and put them in a line,all of us know that it will be 2^N different ways.We call a "N coins sequence" as a Lucky Coins Sequence only if there exists more than two continuous coins's state are same.How many different Lucky Coins Sequences exist?
 

 

Input
There will be sevaral test cases.For each test case,the first line is only a positive integer n,which means n coins put in a line.Also,n not exceed 10^9.
 

 

Output
You should output the ways of lucky coins sequences exist with n coins ,but the answer will be very large,so you just output the answer module 10007.
 

 

Sample Input
3
4
Sample Output
2
6
 
先写个暴力吧,得5时是16,6时是38,7时是86。6=2*2+2,16=6*2+4,38=16*2+6,86=38*2+10。观察后面加的数,恰好是斐波那契数列的值*2。前面说过这种式子是绝对没有通项式的,所以这题得先构造两个新的矩阵。稍加思索可以得到:
A={2 2 2    B={2 0 0
   0 0 0       1 1 1
   0 0 0}      0 1 0} 做法和之前一模一样。
代码:
#include<iostream>
#include<stdio.h>
using namespace std;
#define mdu 10007;
#define N 15
struct matrix
{
    __int64 a[N][N];
};
matrix chu,die;
matrix mul(matrix a1,matrix a2,int ii)
{
    int i,j,k;
    matrix c;
    for(i=1;i<=ii;i++)
      for(j=1;j<=ii;j++)
      {
             c.a[i][j]=0;
           for(k=1;k<=ii;k++)
           {
             c.a[i][j]+=a1.a[i][k]*a2.a[k][j]%mdu;
           }
           c.a[i][j]%=mdu;    
        }
   return c;
}
void qmi(__int64 nn)
{
    int i,j;
    while(nn>0)
    {
        if(nn%2==1) chu=mul(chu,die,3);
        die=mul(die,die,3);
        nn=nn>>1;
    }
}
int main()
{
    __int64 n;
    
    while(scanf("%I64d",&n)!=EOF)
    {
        chu.a[1][1]=2; chu.a[1][2]=2; chu.a[1][3]=2;
        chu.a[2][1]=0; chu.a[2][2]=0; chu.a[2][3]=0;
        chu.a[3][1]=0; chu.a[3][2]=0; chu.a[3][3]=0;
        die.a[1][1]=2; die.a[1][2]=0; die.a[1][3]=0;
        die.a[2][1]=1; die.a[2][2]=1; die.a[2][3]=1;
        die.a[3][1]=0; die.a[3][2]=1; die.a[3][3]=0;
        if(n==0 || n==1 || n==2){
            printf("0\n");
            continue;
        }
        if(n==3){
            printf("2\n");
            continue;
        }
        qmi(n-3);
        printf("%I64d\n",chu.a[1][1]);
    }
    

    return 0;
}
View Code