1355 斐波那契的最小公倍数

1355 斐波那契的最小公倍数

原题连接:
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1355

之前看唐老师题解学习的一个题目。顺便复习一下斐波那契数
今天想到一种相同结论。中间过程略微不一样的方法。

给定 a1,a2,a3....an

计算: LCM(Fa1,Fa2,...Fan)

这个题目其实用到了很多巧妙的结论。(需要你对斐波那契数的诸多性质有一定了解。
我们通常说的的 fib 数是指 F0=0 , F1=1 , n>0
我们在快速计算 mod 意义下的 fib 数事。通常会用下面递推矩阵:

[1110]n[FkFk1]=[Fk+nFk+n1]

我们看矩阵幂后里面的样子:
不需要计算。我们只需要观察。

[1110][acbd]=[a+cab+db]

从纵向看。每一项都是之前两项的的和。
那就有:

[1110]n=[Fn+1FnFnFn1]

那么就有:

[1110]n[1110]k=[Fn+1FnFnFn1][Fk+1FkFkFk1]

矩阵相乘之后你会发现,不需要数学归纳法。我们就有:

Fn+k=FnFk1+Fn+1Fk

这个式子很重要。
首先

F2n=FnFn1+Fn+1Fn

这也就是说。 F2n Fn 的倍数。
在有:

Fkn=F(k1)n1Fn+F(k1)nFn+1

如果。 F(k1)n Fn 的倍数。那么意味着:
Fkn Fn 的倍数。
k=2 显然成立。那么意味着 k=3,4,5,6.. 都成立
即,我们得到这样一个事实: Fkn Fn 的倍数。
那么就有:

Fn , Fk,Fgcd(n,k)

所以:

gcd(Fn,Fk)  Fgcd(n,k)

我们有一个更确定的结论。

即:
gcd(Fn,Fk)=Fgcd(n,k)

证明上面结论我们还需要一个结论: n>1 时 , 恒有 Fn Fn1 互素。
那么(这里我们认为。 nk ):

gcd(Fn,Fk)=gcd(Fk,Fn mod Fk)

其中:
Fn mod Fk=(FnkFk1+Fnk+1Fk) mod Fk=FnkFk1 mod Fk

继续对 Fnk 进行上述操作。我们得到:

Fn mod Fk=Fn mod kFnkk1 mod Fk

所以就有:
gcd(Fn,Fk)=gcd(Fk,Fn mod kFnkk1)

因为: FkFk1

所以:
gcd(Fn,Fk)=gcd(Fk,Fn mod k)

进而我们有:
gcd(Fn,Fk)=Fgcd(n,k)

回到原问题:

lcm(Fn,Fk)=FnFkgcd(Fn,Fk)=FnFkFgcd(n,k)

可以肯定对是。上面对式子是不可用的。因为你会发现。他只适用于计算两个斐波那契数的最小公倍数。而不能计算一个斐波那契数和任意数字的最小公倍数。
因为斐波那契数是非常大的。答案要求对 109+7 取莫。
这就使得即使是朴素的最大公约数算法也是不可行的。
但最大公约数和最小公倍数都可以看作是质因数上的交集和并集。
有关集合之间的求解。多数与反演容斥有关。
试一试?
我们把数字看作素数的集合。
那么对于整数 a,b

则:
ab=gcd(a,b)ab=lcm(a,b)

ab 就是 ab 此时, a b中的补集合就是 ba

那么有:

answer=i=1nFai

(很明显。这种表示得到的是一个多重集合。关于素因子的。而在这这种表示上。多重集合的集合元素个数的和等价于乘积。也就是说。 |a|+|b|=|ab| .一般性的容斥原理不在局限于元素个数的统计。而是集合对应的实值函数。这里。每一个素数的多重集合。都有且只对应一个整数。)

应用一般性的容斥原理:

i=1nFai=i=1nFai1i<jnF1gcd(ai,aj)1i<j<knFgcd(ai,aj,ak)....
上式子没有应用价值。但可以得到很多有用的信息。
对于答案的贡献。最终体现在每一个 fib 数上。
不妨设:
answer=i1Fh[i]i
专注于 h[i] 的计算。

记。在 a[] 中取 k 个数。这k个数的最大公约数等于 n 的方案数量为:
pair(k,n)

记。在 a[] 中取 k 个数。这k个数的最大公约数等于 n 的倍数的方案数量为:
P(k,n)

则:
P(k,n)=n|dpair(k,d)

记。 a[] 中。 n 的倍数的个数为cnt[n]

那么 P(k,n) 等效于在这 cnt[n] 个数中取 k 个数的方案数

则:
P(k,n)=(cnt[n]k)

应用经典反演有:

pair(k,n)=n|dμ(dn)P(k,d)

求和得到 h[n]:
h[n]=k1(1)k+1pair(k,n)=k1(1)k+1n|dμ(dn)P(k,d)=k1n|dμ(dn)P(k,d)(1)k+1=n|dk1μ(dn)P(k,d)(1)k+1=n|dμ(dn)k1(cnt[d]k)(1)k+1

因为:

1(11)cnt[d]=k0(cnt[d]k)(1)k+1

k1(cnt[d]k)(1)k+11=0

所以:
h[n]=n|dμ(dn)[cnt[d]>0]

此刻已经很欣喜了。。。。(剩下的快速幂一波出答案。)

answer=i1Fh[i]i

下面是AC代码

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define Ld(a) ((LL)a)
#define MAXN 1000005
using namespace std;
typedef long long LL;
const LL P=1e9+7;
int Ts[MAXN];
int mu[MAXN];
bool vis[MAXN];
bool G[MAXN];
int pw[MAXN];
void init(int n)
{
    for(int i=3;i<n;i++)
        for(int j=i;j<n;j+=i)
            if(vis[j])
            {
                G[i]=true;
                break;
            }
}
void getMu(int n)
{
    for(int i=1;i<=n;i++)
    {
        int target= i==1?1:0;
        int delta= target-mu[i];
        mu[i]=delta;
        for(int j=i+i;j<=n;j+=i)
            mu[j]+=delta;
    }
}
void init2(LL n)
{
    for(LL i=3;i<n;i++)
        for(LL j=1;j*i<n;j++)
            if(G[i*j])pw[i]+=mu[j];
}
LL extd(LL a,LL b, LL &X, LL &Y)
{
    if(b==0)
    {
        X=1;
        Y=0;
        return a;
    }
    else
    {
        LL r=extd(b,a%b,Y,X);
        Y-=X*(a/b);
        return r;
    }
}
LL pow(LL a,int b)
{
    if(b<0)
    {
        LL y,Iva;
        extd(a,P,Iva,y);
        while(Iva<0)Iva+=P;
        a=Iva;
        b=-b;
    }
    LL temp=1;
    while(b)
    {
        if(b&1) temp=(temp*a)%P;
        a=(a*a)%P;
        b>>=1;
    }
    return temp;
}
int main ()
{
    int n,a,A=1;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a);
        if(a<3)continue;
        vis[a]=true;
        A=max(A,a);
    }
    init(A+1);
    getMu(A);
    init2(A+1);
    LL f1=1,f2=1,f3=2,ans=1;
    for(int i=3;i<=A;i++)
    {
        if(pw[i])
        {
            ans=ans*pow(f3,pw[i]);
            ans%=P;
        }
        f1=f3;
        f3=(f3+f2)%P;
        f2=f1;
    }
    printf("%lld\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值