题目
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]
详细代码如下:
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#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.要充分理解并灵活运用矩阵快速幂的模板,并学会在题目中发现使用。
题目拓展
本题还可在原基础上继续拓展:
题目
算法分析
我们可以考虑一个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]
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#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;
}