[51nod1355][hackerrank]斐波那契的lcm

题目大意

给出n个正整数a1, a2,…… an,求对应的斐波那契数的最小公倍数,由于数字很大,输出Mod 1000000007的结果即可。
例如:1 3 6 9, 对应的斐波那契数为:1 2 8 34, 他们的最小公倍数为136。
2 <= N <= 50000
1 <= ai <= 1000000

解题思路

不看别人的博客真的是什么都不懂…我会 O(n2) O ( n 2 ) !
题目要求的是 lcm(Fa1...Fan) l c m ( F a 1 . . . F a n ) ,其中F为斐波那契数列。
我们需要知道一个性质: gcd(Fi,Fj)=Fgcd(i,j) g c d ( F i , F j ) = F g c d ( i , j )
大致的证明就是:用斐波那契的递推矩阵,我们可以发现 Fn+k=FnFk1+Fn+1Fk F n + k = F n F k − 1 + F n + 1 F k
得到 F2n=Fn(Fn1+Fn+1) F 2 n = F n ( F n − 1 + F n + 1 ) ,那么 F2n F 2 n Fn F n 的倍数。
那么对于 Fi,Fj F i , F j ,他们都是 F(i,j) F ( i , j ) 的倍数。
离结论很近了。
我们考虑:
(Fi,Fj)=(Fj,Fi%Fj) ( F i , F j ) = ( F j , F i % F j )
Fi=FijFj1+Fij+1FjFijFj1(modFj) F i = F i − j F j − 1 + F i − j + 1 F j ≡ F i − j F j − 1 ( m o d F j )
我们对 Fij F i − j 再做同样的操作。
最后就能得出 Fi%jFkj1k=ij F i % j ∗ F j − 1 k , k = ⌊ i j ⌋ ,那么 (Fi,Fj)=(Fj,Fi%jFkj1) ( F i , F j ) = ( F j , F i % j F j − 1 k ) ,又知道 FjFj1 F j 和 F j − 1 互质(用归纳法能证)。
那么就得到 (Fi,Fj)=(Fj,Fi%j) ( F i , F j ) = ( F j , F i % j )
那么 gcd(Fi,Fj)=Fgcd(i,j) g c d ( F i , F j ) = F g c d ( i , j )
知道这个还没法做,求多个数的lcm,不能像两个的lcm直接乘起来除掉gcd,我们考虑怎么做。
考虑质因子的可重集合。比如,并集的操作,S1={2,2,2},S2={3,2}, S1S2=2,2,2,3 S 1 ∪ S 2 = 2 , 2 , 2 , 3
我们知道lcm实际上是所有质因子的并集,而gcd则是交集。记 Si S i 表示 Fai F a i 的质因子集合。
套用经典的容斥原理有:
i=1..nSi=i=1..nSii,jSiSj+i,j,kSiSjSk... ∪ i = 1.. n S i = ∑ i = 1.. n S i − ∑ i , j S i ∩ S j + ∑ i , j , k S i ∩ S j ∩ S k . . .
这里的集合加法,对应到数域就是乘法,那么
lcm(Fai...)=iFaii,jF1gcd(ai,aj)... l c m ( F a i . . . ) = ∏ i F a i ∏ i , j F g c d ( a i , a j ) − 1 . . .
这样并没法直接做,而通常的,对于数论题目,我们考虑每个 Fi F i 对答案的最终贡献的次幂。
设f(i)表示从F_i在答案中的次幂,那么 f(i)=k(1)k+1p[1..k][gcd(ap[1]..ap[k])=i] f ( i ) = ∑ k ( − 1 ) k + 1 ∑ p [ 1.. k ] [ g c d ( a p [ 1 ] . . a p [ k ] ) = i ]
看到gcd,考虑使用莫比乌斯反演。
g(i)=k(1)k+1p[1..k] [i|gcd(ap[1]..ap[k])] g ( i ) = ∑ k ( − 1 ) k + 1 ∑ p [ 1.. k ]   [ i | g c d ( a p [ 1 ] . . a p [ k ] ) ]
g(i)是非常容易表示的,假设a[]里有cnt[i]个数是i的倍数,那么 g(i)=k1(1)k+1Ckcnt[i] g ( i ) = ∑ k ≥ 1 ( − 1 ) k + 1 C c n t [ i ] k
反演并推导。
f(i)=Dμ(D)g(iD) f ( i ) = ∑ D μ ( D ) g ( i D )
=Dμ(D)k1Ckcnt[iD](1)k+1 = ∑ D μ ( D ) ∑ k ≥ 1 C c n t [ i D ] k ( − 1 ) k + 1
=Dμ(D)(1)k1Ckcnt[iD](1)k = ∑ D μ ( D ) ( − 1 ) ∑ k ≥ 1 C c n t [ i D ] k ( − 1 ) k
注意到若cnt[iD]=0,后面部分为0
否则 k1Ckcnt[iD](1)k=(k0Ckcnt[iD](1)k)1=(11)cnt1=1 ∑ k ≥ 1 C c n t [ i D ] k ( − 1 ) k = ( ∑ k ≥ 0 C c n t [ i D ] k ( − 1 ) k ) − 1 = ( 1 − 1 ) c n t − 1 = − 1
那么 f(i)=Dμ(D)[cnt[iD]>0] f ( i ) = ∑ D μ ( D ) [ c n t [ i D ] > 0 ]
答案是 i1F[i]f(i) ∏ i ≥ 1 F [ i ] f ( i ) ,有这些已经可以直接做了。

代码

#include<cstdio> 
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
typedef double db;
const int N=1e6+5,mo=1e9+7;
int n,x,i,j,mx,ans;
int miu[N],pd[N],pri[N],fib[N],cnt[N],f[N];
void predo(int n)
{
    int i,j,t;
    miu[1]=1;
    fo(i,2,n)
    {
        if (!pd[i])
        {
            pri[++pri[0]]=i;
            miu[i]=-1;
        }
        fo(j,1,pri[0])
        {
            if (1ll*i*pri[j]>n) break;
            t=i*pri[j];
            pd[t]=1;
            miu[t]=-miu[i];
            if (i%pri[j]==0)
            {
                miu[t]=0;
                break;
            }
        }
    }
    fib[2]=fib[1]=1;
    fo(i,3,n) fib[i]=(fib[i-1]+fib[i-2])%mo;
}
int ksm(int x,int y)
{
    int ret=1;
    if (y<0) y=1ll*abs(y)*(mo-2)%(mo-1);
    while (y)
    {
        if (y&1) ret=1ll*ret*x%mo;
        y>>=1;
        x=1ll*x*x%mo;
    }
    return ret;
}
int main()
{
    freopen("t13.in","r",stdin);
    //freopen("t13.out","w",stdout);
    predo(1e6);
    scanf("%d",&n);
    fo(i,1,n)
    {
        scanf("%d",&x);
        cnt[x]++;
        cmax(mx,x);
    }
    fo(i,1,mx)
        for (j=i+i;j<=mx;j+=i)
            cnt[i]+=cnt[j];
    ans=1;
    fo(i,1,mx)
    {
        for(j=i;j<=mx;j+=i)
            f[i]+=miu[j/i]*(cnt[j]>0);
        ans=1ll*ans*ksm(fib[i],f[i])%mo;
    }
    if (ans<0) ans+=mo;
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值