利用矩阵&快速幂解决斐波那契数列相关题目小结

年轻的时候在博客园上发布的一篇文章,现在把它搬运过来,也方便我以后复习这块知识。


在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;
}

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



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;
}


我们再来看一题:

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值