[51Nod1355]斐波那契的最小公倍数

V.[51Nod1355]斐波那契的最小公倍数

引理1. 设 fi 表示斐波那契数列中第 i 项,则 gcd(fi,fj)=fgcd(i,j)

一种证明方法是打表

另一种证明方法是,首先有 fi+j=fi1fj+fifj+1(这个可以通过暴力拆斐波那契数得到)。显然,fi1figcd1fj+1fjgcd1(这个是真的简单归纳就能得出),故 gcd(fi+j,fj)=gcd(fi,fj)。发现这就是求 gcd 的辗转相减法的形式,因此证毕。

引理2. lcm{S}=TSgcd{T}(1)|T|+1

考虑对每一个质数进行minmax容斥。在枚举一个质数后,考虑 lcm 中该质数的次数,为 maxxS{dx},其中 dxx 中此质数的次数。

于是 maxxS{dx}=TS(1)|T|+1minxT{dx}。在将其移回指数上后,便得到了上述结论。

结合引理1,2,我们得到

lcm{fS}=TSfgcd{T}(1)|T|+1

现在考虑对于每个 gcd,有多少符合条件的 T,以及有多少个 |T| 为奇,多少个为偶。

考虑我们现在强行构造出一个数列 g 满足 fnd|ngd(mod109+7)。于是就有

TSfgcd{T}(1)|T|+1=TS(d|gcd{T}gd)(1)|T|+1=d(gd)TS,d|gcd{T}(1)|T|+1

现在考虑把 gd 的指数掏出来,是 TS,d|gcd{T}(1)|T|+1

考虑 S 中所有是 d 的倍数的数的数量,设其为 p。则上式等价于 k=1p(pk)(1)k+1。显然,当 p0 时,此式为 0;否则,其可被改写成 1+k=0p(pk)(1)k+1。依据二项式定理,后面那一大坨,在 p>0 时为 0。故其就等价于 [p>0],也即存在 d 的倍数。

于是问题转换为 xS,d|xgd。显然就可以简单求出。问题转换为如何构造 g

因为 fn=d|ngd,或许会想到狄利克雷卷积。但是狄利克雷卷积中是 而非 ,因此不能使用。更好的方式是把除了 gn 以外所有项全部移到另一边,得到 gn=fnd|n,dngd,可以 O(nn)O(nlogn) 地递推。

总时间复杂度可以做到 O(nlogn)

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int ksm(int x,int y=mod-2){int z=1;for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;return z;}
int n,m,a[50100],g[1001000],res=1;
bool occ[1001000];
int main(){
	scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]),m=max(m,a[i]),occ[a[i]]=true;
	g[0]=0,g[1]=1;for(int i=2;i<=m;i++)g[i]=(g[i-1]+g[i-2])%mod;
	for(int i=1;i<=m;i++){
		int inv=ksm(g[i]);
		for(int j=2*i;j<=m;j+=i)g[j]=1ll*g[j]*inv%mod;
		for(int j=i;j<=m;j+=i)if(occ[j]){res=1ll*res*g[i]%mod;break;}
	}
	printf("%d\n",res);
	return 0;
}

posted @   Troverld  阅读(133)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示