LOJ #10222. 「一本通 6.5 例 4」佳佳的 Fibonacci 题解

LOJ #10222. 「一本通 6.5 例 4」佳佳的 Fibonacci 题解

题目传送门

如果之前推过斐波那契数列前缀和就更好做(所以题目中给出了)。

斐波那契数列前缀和题目链接

先来推一下斐波那契数列前缀和:

i=1nf(i)

其中 f(i) 表示Fibonacci数列第 i 项。

直接推式子:

s(x)=i=1xf(i)

将右边一项项展开得出

f(1)=1

f(2)=1

f(3)=f(1)+f(2)

f(4)=f(2)+f(3)

...

f(n)=f(n2)+f(n1)

这些式子左右两边分别再加回去得出

s(n)=1+1+f(1)+f(n1)+2i=2n2f(i)

把其中一个 1 变成 f(1) 再和另一个 f(1) 加到 2i=2n2f(i) 里面,得出

s(n)=1+f(n1)+2i=1n2f(i)

s(n)=1+f(n1)+2s(n2)

s(n)s(n2)s(n2)=1+f(n1)

f(n)+f(n1)s(n2)=1+f(n1)

f(n)s(n2)=1

s(n2)=f(n)1

n2 变成 n 可得

s(n)=f(n+2)1

注意到 f 是可以直接矩阵快速幂求的。这个时候就可以在 O(logn) 的时间复杂度求得 s(n) 了。


这个时候回来看本题:

对于 T(n) 来说,f(n) 被计算了 n 次,f(n1) 被计算了 (n1) 次...

T(n)=i=1nf(i)i

可以用后缀和的形式来表示这个式子,计 s2(x)=i=xnf(i)

所以上面的式子可以进一步转化成这个后缀和的形式

T(n)=i=1ns2(i)

可是 n 又不确定,又不会推后缀和,应该怎么求呢?

不会后缀和,但是我们会前缀和啊!

s 表示上述式子即为

T(n)=i=1ns(n)s(i1)

s(n) 提出来:

T(n)=ns(n)i=1ns(i1)

代入 s(i)=f(i+2)1

T(n)=nf(n+2)ni=1n(f(i+1)1)

里面的 1 提出来

T(n)=nf(n+2)ni=1nf(i+1)+n

之后就很简单了。

T(n)=nf(n+2)ni=2n+1f(i)+n

T(n)=nf(n+2)ni=2n+1f(i)+f(1)f(1)+n

T(n)=nf(n+2)ni=1n+1f(i)f(1)+n

化简一下

T(n)=nf(n+2)s(n+1)1

T(n)=nf(n+2)(f(n+3)1)1

T(n)=nf(n+2)f(n+3)+2

矩阵快速幂求 f(n+2)f(n+3) 就能 O(logn) 的时间复杂度求出 T(n) 了。

因为最后的式子里面有个减法,可以提前在减法之前加上一个 m 来防止负数取模的情况发生。

参考 Code

#include<iostream>
#include<cstdio>
#define ll long long
int n,m;
struct Matrix {
	ll mat[3][3];
	int n,m;
	void memset() {
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				mat[i][j]=0;
	}
};
Matrix mul(Matrix x,Matrix y)
{
	Matrix z;
	z.n=x.n;
	z.m=y.m; 
	z.memset();
	for(int i=1;i<=z.n;i++)
		for(int j=1;j<=z.m;j++)
			for(int k=1;k<=x.m;k++)
				z.mat[i][j]=(z.mat[i][j]+x.mat[i][k]*y.mat[k][j])%m;
	return z; 
}
Matrix qpow(Matrix base,int y)
{
	Matrix ans;
	ans.n=ans.m=2;
	ans.memset();
	for(int i=1;i<=2;i++)
		ans.mat[i][i]=1;
	while(y)
	{
		if(y&1) ans=mul(ans,base);
		base=mul(base,base);
		y>>=1;
	}
	return ans;
}
ll f(int n)
{
	Matrix ans,base;
	ans.n=1;
	ans.m=2;
	base.n=base.m=2;
	ans.memset();
	base.memset();
	ans.mat[1][1]=1;ans.mat[1][2]=1;
	base.mat[1][1]=0;base.mat[1][2]=1;
	base.mat[2][1]=1;base.mat[2][2]=1;
	base=qpow(base,n-2);
	ans=mul(ans,base);
	return ans.mat[1][2];
} 
int main()
{
	scanf("%d%d",&n,&m);
	printf("%lld",(n*f(n+2)%m-f(n+3)+m+2)%m);
	return 0;
}
posted @   do_while_true  阅读(239)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 34 minutes 13 seconds

点击右上角即可分享
微信分享提示