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


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

    引理1. 设 (f_i) 表示斐波那契数列中第 (i) 项,则 (gcd(f_i,f_j)=f_{gcd(i,j)})

    一种证明方法是打表

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

    引理2. (operatorname{lcm}{mathbb S}=prodlimits_{mathbb{Tsubseteq S}}gcd{mathbb T}^{(-1)^{|mathbb T|+1}})

    考虑对每一个质数进行minmax容斥。在枚举一个质数后,考虑 ( ext{lcm}) 中该质数的次数,为 (maxlimits_{xinmathbb S}{d_x}),其中 (d_x)(x) 中此质数的次数。

    于是 (maxlimits_{xinmathbb S}{d_x}=sumlimits_{mathbb{Tsubseteq S}}(-1)^{|T|+1}minlimits_{xinmathbb T}{d_x})。在将其移回指数上后,便得到了上述结论。

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

    [operatorname{lcm}{f_mathbb S}=prodlimits_{mathbb{Tsubseteq S}}f_{gcd{mathbb T}}^{(-1)^{|mathbb T|+1}} ]

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

    考虑我们现在强行构造出一个数列 (g) 满足 (f_nequivprodlimits_{d|n}g_dpmod{10^9+7})。于是就有

    [prodlimits_{mathbb{Tsubseteq S}}f_{gcd{mathbb T}}^{(-1)^{|mathbb T|+1}}=prodlimits_{mathbb{Tsubseteq S}}(prodlimits_{d|gcd{mathbb T}}g_d)^{(-1)^{|mathbb T|+1}}=prodlimits_d(g_d)^{sumlimits_{mathbb{Tsubseteq S},d|gcd{mathbb T}}(-1)^{|mathbb T|+1}} ]

    现在考虑把 (g_d) 的指数掏出来,是 (sumlimits_{mathbb{Tsubseteq S},d|gcd{mathbb T}}(-1)^{|mathbb T|+1})

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

    于是问题转换为 (prodlimits_{exists xinmathbb S,d|x}g_d)。显然就可以简单求出。问题转换为如何构造 (g)

    因为 (f_n=prodlimits_{d|n}g_d),或许会想到狄利克雷卷积。但是狄利克雷卷积中是 (sum) 而非 (prod),因此不能使用。更好的方式是把除了 (g_n) 以外所有项全部移到另一边,得到 (g_n=dfrac{f_n}{prodlimits_{d|n,d eq n}g_d}),可以 (O(nsqrt{n}))(O(nlog n)) 地递推。

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

    代码:

    #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
    ",res);
    	return 0;
    }
    

  • 相关阅读:
    邮箱正则表达式写法
    java中的的正则表达式
    Java中重载(overload)和重写(override)的区别
    内部类的使用规范
    Java静态代码块(static block)调用陷阱小记
    sychronized关键字的使用
    关于java中一次编译多个源文件时的编译顺序的问题
    java中内部类的访问调用
    map的三种遍历方法
    Java堆.栈和常量池
  • 原文地址:https://www.cnblogs.com/Troverld/p/14636954.html
Copyright © 2020-2023  润新知