年轻的时候在博客园上发布的一篇文章,现在把它搬运过来,也方便我以后复习这块知识。
在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
F[0] = a
F[1] = b
F[n] = F[n-1] * F[n-2] ( n > 1 )
现在给出a, b, n,你能求出F[n]的值吗?
每组数据占一行,包含3个整数a, b, n( 0 <= a, b, n <= 10^9 )
#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
#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;
}