数论——矩阵乘法 + P1962 斐波那契数列 + P1349 广义斐波那契数列

矩阵乘法 + P1962 斐波那契数列

  • 题目
  • 算法分析
  • Code
  • 反思与总结
  • 题目拓展
    • 题目
    • 算法分析
    • Code

题目

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(n1)+F(n2)

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[n1]+f[n2],f[1]=f[2]=1
我们可以考虑一个1×2的矩阵【 f [ n − 1 ] , f [ n − 2 ] f[n-1],f[n-2] f[n1],f[n2]】。根据 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[n1]】。
即:
f [ n − 1 ] , f [ n − 2 ] f[n-1],f[n-2] f[n1],f[n2] ∗ A = *A = A= f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n1] = = = f [ n − 1 ] + f [ n − 2 ] , f [ n − 1 ] f[n-1]+f[n-2],f[n-1] f[n1]+f[n2],f[n1]
很容易构造出这个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(n2)= f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n1]
这个矩阵的第一个元素 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
#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[n1],f[n2]】。根据 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[n1]】。
即:
f [ n − 1 ] , f [ n − 2 ] f[n-1],f[n-2] f[n1],f[n2] ∗ A = *A = A= f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n1] = = = f [ n − 1 ] + f [ n − 2 ] , f [ n − 1 ] f[n-1]+f[n-2],f[n-1] f[n1]+f[n2],f[n1]
很容易构造出这个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(n2)= f [ n ] , f [ n − 1 ] f[n],f[n-1] f[n],f[n1]
这个矩阵的第一个元素 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]=a1A.mp[1][1]+a2A.mp[1][2]

Code

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

你可能感兴趣的:(数学,算法,c++,数学,矩阵,线性代数)