题目大意
给出n个正整数a1, a2,…… an,求对应的斐波那契数的最小公倍数,由于数字很大,输出Mod 1000000007的结果即可。
例如:1 3 6 9, 对应的斐波那契数为:1 2 8 34, 他们的最小公倍数为136。
2 <= N <= 50000
1 <= ai <= 1000000
解题思路
不看别人的博客真的是什么都不懂…我会
!
题目要求的是
,其中F为斐波那契数列。
我们需要知道一个性质:
。
大致的证明就是:用斐波那契的递推矩阵,我们可以发现
。
得到
,那么
是
的倍数。
那么对于
,他们都是
的倍数。
离结论很近了。
我们考虑:
而
我们对
再做同样的操作。
最后就能得出
,那么
,又知道
互质(用归纳法能证)。
那么就得到
那么
。
知道这个还没法做,求多个数的lcm,不能像两个的lcm直接乘起来除掉gcd,我们考虑怎么做。
考虑质因子的可重集合。比如,并集的操作,S1={2,2,2},S2={3,2},
我们知道lcm实际上是所有质因子的并集,而gcd则是交集。记
表示
的质因子集合。
套用经典的容斥原理有:
这里的集合加法,对应到数域就是乘法,那么
这样并没法直接做,而通常的,对于数论题目,我们考虑每个
对答案的最终贡献的次幂。
设f(i)表示从F_i在答案中的次幂,那么
看到gcd,考虑使用莫比乌斯反演。
设
g(i)是非常容易表示的,假设a[]里有cnt[i]个数是i的倍数,那么
反演并推导。
注意到若cnt[iD]=0,后面部分为0
否则
那么
答案是
,有这些已经可以直接做了。
代码
#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);
}