题目大意
给出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=FnFk−1+Fn+1Fk
F
n
+
k
=
F
n
F
k
−
1
+
F
n
+
1
F
k
。
得到
F2n=Fn(Fn−1+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=Fi−jFj−1+Fi−j+1Fj≡Fi−jFj−1(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
)
我们对
Fi−j
F
i
−
j
再做同样的操作。
最后就能得出
Fi%j∗Fkj−1,k=⌊ij⌋
F
i
%
j
∗
F
j
−
1
k
,
k
=
⌊
i
j
⌋
,那么
(Fi,Fj)=(Fj,Fi%jFkj−1)
(
F
i
,
F
j
)
=
(
F
j
,
F
i
%
j
F
j
−
1
k
)
,又知道
Fj和Fj−1
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},
S1∪S2=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..nSi−∑i,jSi∩Sj+∑i,j,kSi∩Sj∩Sk...
∪
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...)=∏iFai∏i,jF−1gcd(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+1∑p[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+1∑p[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)=∑k≥1(−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)∑k≥1Ckcnt[iD](−1)k+1
=
∑
D
μ
(
D
)
∑
k
≥
1
C
c
n
t
[
i
D
]
k
(
−
1
)
k
+
1
=∑Dμ(D)(−1)∑k≥1Ckcnt[iD](−1)k
=
∑
D
μ
(
D
)
(
−
1
)
∑
k
≥
1
C
c
n
t
[
i
D
]
k
(
−
1
)
k
注意到若cnt[iD]=0,后面部分为0
否则
∑k≥1Ckcnt[iD](−1)k=(∑k≥0Ckcnt[iD](−1)k)−1=(1−1)cnt−1=−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
]
答案是
∏i≥1F[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);
}