NEFU OJ 2348 小蓝与斐波拉契数列

本文讲解了如何使用矩阵快速幂技巧解决大范围斐波那契数列问题,涉及周期计算、Fibonacci数列的周期性特点,并提供了代码示例。关键步骤包括找到F(n)对周期T1和T2的计算,利用矩阵乘法加速计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Time Limit:1000ms
Memory Limit:65535K

Description

在这里插入图片描述

Samples

Input 1

6

Output 1

21

Input 2

99999

Output 2

803980321

Hint

1<=n<=100000

Solution

分析一下题目,题目要求斐波那契的第 F(n) 项,F(F(n)) 的值。
数据范围很大,所以 显然 易知 易证 是有规律的。。。

首先设 t = F ( n ) t=F(n) t=F(n) 计算 n n n F ( t ) F(t) F(t) 的周期 T 1 T1 T1
F ( F ( n ) ) = F ( t   m o d   T 1 )   m o d   M O D F(F(n)) = F(t \bmod T1) \bmod MOD F(F(n))=F(tmodT1)modMOD

然后再计算 t t t n n n T 1 T1 T1 的周期 T 2 T2 T2
F ( F ( n ) ) = F ( F ( n   m o d   T 2 )   m o d   T 1 )   m o d   M O D F(F(n)) = F(F(n \bmod T2) \bmod T1) \bmod MOD F(F(n))=F(F(nmodT2)modT1)modMOD

经过周期的计算发现 T 1 = 2000000016 T1=2000000016 T1=2000000016 仍然很大,所以可以用矩阵快速幂防止递推超时。
矩阵快速幂计算斐波那契数列原理详见 这篇文章

Code

Part 1 周期计算

代码部分数组 a [ p ] a[p] a[p] 为第 n n n 项, a [ ! p ] a[!p] a[!p] 为第 n + 1 n+1 n+1,因此周期为 n − 1 n-1 n1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
	ll n=0,a[2]={0,1},p=0,mod=1e9+7,cnt=0,T1;
	while(1)
	{
		a[p]=(a[0]+a[1])%mod;
		p=!p;
		n++;
		if(a[p]==1&&a[!p]==1)cnt++;
		if(cnt==2)break;
	}
	T1=n-1;
	cout<<"T1 = "<<T1<<"\n";
	a[0]=0;a[1]=1;p=0;cnt=0;n=0;
	while(1)
	{
		a[p]=(a[0]+a[1])%T1;
		p=!p;
		n++;
		if(a[p]==1&&a[!p]==1)cnt++;
		if(cnt==2)break;
	}
	cout<<"T2 = "<<n-1;
	return 0;
}

运行结果:
在这里插入图片描述

Part 2 题目代码

注意:本题数据范围 1 ≤ n ≤ 100000 1 \le n\le 100000 1n100000 不需要考虑 n = 0 n= 0 n=0 的情况。
如果 n n n 的数据范围包含 0 则需要额外进行判断。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; 

ll n,k;
ll T1=2000000016;
ll T2=329616;
ll mod=1e9+7;

class Matrix
{
public:
	static Matrix unit;
	ll data[2][2]={{0}};
	int n,m;
	
	Matrix(int nIn,int mIn):n(nIn), m(mIn){}
	
	Matrix mul(Matrix &mat, ll md)
	{
		Matrix tmp=Matrix::unit;
		tmp.n=this->n;
		tmp.m=mat.m;
		for(int i=0;i<this->n;i++)
		{
			for(int j=0;j<mat.m;j++)
			{
				tmp.data[i][j] = 0;
				for(int k=0;k<this->m;k++)
				{
					tmp.data[i][j] += this->data[i][k]*mat.data[k][j]%md;
					tmp.data[i][j] %= md;
				}
			}
		}
		return tmp;
	}
	
	void print()
	{
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				if(j!=0)cout<<" ";
				cout<<this->data[i][j];
			}
			cout<<"\n";
		}
	}
};

Matrix Matrix::unit(2,2);
Matrix mat(2,2);

Matrix qpow(Matrix m1,ll b,ll mod)
{
	Matrix ans=Matrix::unit;
	while(b>0)
	{
		if(b&1)ans=ans.mul(m1,mod);
		m1=m1.mul(m1,mod);
		b>>=1;
	}
	return ans;
}

int main()
{
	cin>>n;
	Matrix::unit.data[0][0]=Matrix::unit.data[1][1]=1;
	mat.data[0][0]=mat.data[0][1]=mat.data[1][0]=1;
	mat=qpow(mat,n%T2-1,T1);
	n=mat.data[0][0];
	mat.data[0][0]=mat.data[0][1]=mat.data[1][0]=1;
	mat.data[1][1]=0;
	mat=qpow(mat,n-1,mod);
	cout<<mat.data[0][0];
	return 0;
}


参考文章:https://www.cnblogs.com/dfsac/p/7587784.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值