51Nod1355:斐波那契的最小公倍数 (min-max容斥+Mobius反演)

本文介绍了一种利用Fib数列的性质和Mobius反演来求解一组数的最小公倍数的方法。通过分析Fib数列的特性,使用归纳法证明了Fib数列的最大公约数的性质,并给出了具体的实现代码。

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

传送门

题解:
对于fib数列有 gcd(fi,fj)=fgcd(i,j) gcd ( f i , f j ) = f gcd ( i , j ) (可用归纳法证明)。
那么对于 gcd(f{T}) gcd ( f { T } ) 显然等于 fgcd{T} f gcd { T }

怎么求lcm? 直接min-max对指数容斥即可,易得:

lcmf{T}=STf(1)|S|+1gcd{S} lcm f { T } = ∏ S ∈ T f gcd { S } ( − 1 ) | S | + 1

为了消掉gcd,我们用Mobius反演(乘法意义下),构造 g g ,使得fn=d|ngn,那么 gn=fnd|n,dng1d g n = f n ∏ d | n , d ≠ n g d − 1 或者 gn=d|nfμ(nd)d g n = ∏ d | n f d μ ( n d )

那么有:

lcmf{T}===STf(1)|S|+1gcd{S}ST(d|gcd{S}gd)(1)|S|+1dgST,d|gcd{S}(1)|S|+1d lcm f { T } = ∏ S ∈ T f gcd { S } ( − 1 ) | S | + 1 = ∏ S ∈ T ( ∏ d | gcd { S } g d ) ( − 1 ) | S | + 1 = ∏ d g d ∑ S ∈ T , d | gcd { S } ( − 1 ) | S | + 1

显然如果有任意一个数 ai a i 使得整除 d|ai d | a i ,那么最后 gd g d 会被计算1次。 直接看哪些 gd g d 有倍数然后乘起来就好了。

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+50, mod=1e9+7;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return rs;}
int n,a[N],mx,fib[N],g[N],ok[N];
int main() {
    cin>>n; for(int i=1;i<=n;i++) cin>>a[i], mx=max(mx,a[i]), ok[a[i]]=1;
    fib[1]=1; for(int i=2;i<=mx;i++) fib[i]=add(fib[i-1],fib[i-2]);
    for(int i=1;i<=mx;i++) g[i]=fib[i];
    int ans=1;
    for(int i=1;i<=mx;i++) {
        const int inv=power(g[i],mod-2);
        for(int j=2;j*i<=mx;j++) g[i*j]=mul(g[i*j],inv);
    }
    for(int i=1;i<=mx;i++)
        for(int j=1;j*i<=mx;j++) 
            if(ok[i*j]) {ans=mul(ans,g[i]); break;}
    cout<<ans<<endl;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值