[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 ( n 2 ) !
题目要求的是 l c m ( F a 1 . . . F a n ) ,其中F为斐波那契数列。
我们需要知道一个性质: g c d ( F i , F j ) = F g c d ( i , j )
大致的证明就是:用斐波那契的递推矩阵,我们可以发现 F n + k = F n F k 1 + F n + 1 F k
得到 F 2 n = F n ( F n 1 + F n + 1 ) ,那么 F 2 n F n 的倍数。
那么对于 F i , F j ,他们都是 F ( i , j ) 的倍数。
离结论很近了。
我们考虑:
( F i , F j ) = ( F j , F i % F j )
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 )
我们对 F i j 再做同样的操作。
最后就能得出 F i % j F j 1 k k = i j ,那么 ( F i , F j ) = ( F j , F i % j F j 1 k ) ,又知道 F j F j 1 互质(用归纳法能证)。
那么就得到 ( F i , F j ) = ( F j , F 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}, S 1 S 2 = 2 , 2 , 2 , 3
我们知道lcm实际上是所有质因子的并集,而gcd则是交集。记 S i 表示 F a i 的质因子集合。
套用经典的容斥原理有:
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 . . .
这里的集合加法,对应到数域就是乘法,那么
l c m ( F a i . . . ) = i F a i i , j F g c d ( a i , a j ) 1 . . .
这样并没法直接做,而通常的,对于数论题目,我们考虑每个 F i 对答案的最终贡献的次幂。
设f(i)表示从F_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 + 1 p [ 1.. k ]   [ i | g c d ( a p [ 1 ] . . a p [ k ] ) ]
g(i)是非常容易表示的,假设a[]里有cnt[i]个数是i的倍数,那么 g ( i ) = k 1 ( 1 ) k + 1 C c n t [ i ] k
反演并推导。
f ( i ) = D μ ( D ) g ( i D )
= D μ ( D ) k 1 C c n t [ i D ] k ( 1 ) k + 1
= D μ ( D ) ( 1 ) k 1 C c n t [ i D ] k ( 1 ) k
注意到若cnt[iD]=0,后面部分为0
否则 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 ) [ c n t [ i D ] > 0 ]
答案是 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);
}

猜你喜欢

转载自blog.csdn.net/zltjohn/article/details/80232626