P1962 斐波那契数列 https://www.luogu.com.cn/problem/P1962
首先本题运用到的核心算法为 矩阵乘法
矩阵乘法的相关介绍请见:数论——矩阵乘法
本题运用矩阵乘法解决斐波那契数列,算法分析如下:
F i b o n a c c i Fibonacci Fibonacci数列: F ( 0 ) = 1 , F ( 1 ) = 1 , F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(0)=1 , F(1)=1 , F(n)=F(n-1)+F(n-2) F(0)=1,F(1)=1,F(n)=F(n−1)+F(n−2)
F i b o n a c c i Fibonacci Fibonacci数列 f [ n ] = f [ n − 1 ] + f [ n − 2 ] , f [ 1 ] = f [ 2 ] = 1 f[n]=f[n-1]+f[n-2],f[1]=f[2]=1 f[n]=f[n−1]+f[n−2],f[1]=f[2]=1
我们可以考虑一个1×2的矩阵【 f [ n − 1 ] , f [ n − 2 ] f[n-1],f[n-2] f[n−1],f[n−2]】。根据 F i b o n a c c i Fibonacci Fibonacci数列的递推关系,我们可以通过乘以一个2×2的矩阵A,得到矩阵:【 f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n−1]】。
即:
【 f [ n − 1 ] , f [ n − 2 ] f[n-1],f[n-2] f[n−1],f[n−2]】 ∗ A = *A = ∗A= 【 f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n−1]】 = = =【 f [ n − 1 ] + f [ n − 2 ] , f [ n − 1 ] f[n-1]+f[n-2],f[n-1] f[n−1]+f[n−2],f[n−1]】
很容易构造出这个2×2矩阵 A A A,即:
1 1
1 0
所以,有【 f [ 2 ] , f [ 1 ] f[2],f[1] f[2],f[1]】 × A = ×A= ×A=【 f [ 3 ] , f [ 2 ] f[3],f[2] f[3],f[2]】。又因为矩阵乘法满足结合律,故有:【 f [ 2 ] , f [ 1 ] f[2],f[1] f[2],f[1]】 × A ( n − 2 ) = ×A ^{(n-2)} = ×A(n−2)=【 f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n−1]】
这个矩阵的第一个元素 f [ n ] f[n] f[n]即为所求。
f [ n ] = A . m p [ 1 ] [ 1 ] + A . m p [ 1 ] [ 2 ] f[n]=A.mp[1][1]+A.mp[1][2] f[n]=A.mp[1][1]+A.mp[1][2]
详细代码如下:
#include
#include
#include
#include
#define ll long long
#define rg register
using namespace std;
inline ll sread()
{
ll x=0,f=1;char c=getchar();
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
return f*x;
}
const int p=1000000000+7;
int n=2;
ll k;
struct node{
ll mp[105][105];
};
node mul(node a,node b)
{
node c;
for(rg int i=1;i<=n;++i)
{
for(rg int j=1;j<=n;++j)
{
c.mp[i][j]=0;
for(rg ll k=1;k<=n;++k)
{
c.mp[i][j]+=((a.mp[i][k]%p)*(b.mp[k][j]%p))%p;
}
}
}
return c;
}
node quickpow(node mp,ll b)
{
node ans,base=mp;
for(rg int i=1;i<=n;++i) ans.mp[i][i]=1;
while(b)
{
if(b&1) ans=mul(ans,base);
base=mul(base,base);
b>>=1;
}
return ans;
}
int main()
{
k=sread();
node G,_ans;
G.mp[1][1]=1; G.mp[1][2]=1; G.mp[2][1]=1; G.mp[2][2]=0;
ll ans_;
if(k>=2)
{
_ans=quickpow(G,k-2);
ans_=(_ans.mp[1][1]+_ans.mp[1][2])%p;
}
if(k==1) ans_=1;
printf("%lld",ans_);
return 0;
}
1.注意考虑 k = 1 k=1 k=1 的特殊情况,否则会导致死循环。
2.根据题目条件要适时开 l o n g l o n g long long longlong
3.要充分理解并灵活运用矩阵快速幂的模板,并学会在题目中发现使用。
本题还可在原基础上继续拓展:
Luogu:P1349 广义斐波那契数列
我们可以考虑一个1×2的矩阵【 f [ n − 1 ] , f [ n − 2 ] f[n-1],f[n-2] f[n−1],f[n−2]】。根据 F i b o n a c c i Fibonacci Fibonacci数列的递推关系,我们可以通过乘以一个2×2的矩阵A,得到矩阵:【 f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n−1]】。
即:
【 f [ n − 1 ] , f [ n − 2 ] f[n-1],f[n-2] f[n−1],f[n−2]】 ∗ A = *A = ∗A= 【 f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n−1]】 = = =【 f [ n − 1 ] + f [ n − 2 ] , f [ n − 1 ] f[n-1]+f[n-2],f[n-1] f[n−1]+f[n−2],f[n−1]】
很容易构造出这个2×2矩阵 A A A,即:
p p p q q q
1 1 1 0 0 0
所以,有【 f [ 2 ] , f [ 1 ] f[2],f[1] f[2],f[1]】 × A = ×A= ×A=【 f [ 3 ] , f [ 2 ] f[3],f[2] f[3],f[2]】。又因为矩阵乘法满足结合律,故有:【 f [ 2 ] , f [ 1 ] f[2],f[1] f[2],f[1]】 × A ( n − 2 ) = ×A ^{(n-2)} = ×A(n−2)=【 f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n−1]】
这个矩阵的第一个元素 f [ n ] f[n] f[n]即为所求。
f [ n ] = a 1 ∗ A . m p [ 1 ] [ 1 ] + a 2 ∗ A . m p [ 1 ] [ 2 ] f[n]=a1*A.mp[1][1]+a2*A.mp[1][2] f[n]=a1∗A.mp[1][1]+a2∗A.mp[1][2]
#include
#include
#include
#include
#define ll long long
#define rg register
using namespace std;
inline ll sread()
{
ll x=0,f=1;char c=getchar();
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
return f*x;
}
int n=2;
ll r1,r2,a1,a2,k,p;
struct node{
ll mp[105][105];
};
node mul(node a,node b)
{
node c;
for(rg ll i=1;i<=n;++i)
{
for(rg ll j=1;j<=n;++j)
{
c.mp[i][j]=0;
for(rg ll k=1;k<=n;++k)
{
c.mp[i][j]+=((a.mp[i][k]%p)*(b.mp[k][j]%p))%p;
}
}
}
return c;
}
node quickpow(node mp,ll b)
{
node ans,base=mp;
for(rg ll i=1;i<=n;++i) ans.mp[i][i]=1;
while(b)
{
if(b&1) ans=mul(ans,base);
base=mul(base,base);
b>>=1;
}
return ans;
}
int main()
{
r1=sread(); r2=sread();
a1=sread(); a2=sread();
k=sread(); p=sread();
node G,_ans;
G.mp[1][1]=r1; G.mp[1][2]=r2; G.mp[2][1]=1; G.mp[2][2]=0;
ll ans_;
if(k>=2)
{
_ans=quickpow(G,k-2);
ans_=(a2*_ans.mp[1][1]+a1*_ans.mp[1][2])%p;
}
if(k==1) ans_=a1;
printf("%lld",ans_);
return 0;
}