【分析】
对每个质因数 (p_i) 进行 min-max 容斥得:
(displaystyle max_{p_i}(S)=sum_{varnothingsubset Tsubseteq S}(-1)^{|T|-1}min_{p_i}(T))
故 (displaystyle p_i^{displaystyle max_{p_i}(S)}=p_i^{displaystyle sum_{varnothingsubset Tsubseteq S}(-1)^{|T|-1}min_{p_i}(T)})
对所有 (p_i) 累乘得:
(egin{aligned} ext{lcm } fib_{{S}}&=prod_i p_i^{displaystyle max_{p_i}(T)} \&=prod_i p_i^{displaystyle sum_{varnothingsubset Tsubseteq S}(-1)^{|T|-1}min_{p_i}(T)} \&=prod_{varnothing subset Tsubseteq S} (prod_i p_i^{displaystyle min_{p_i}(T)})^{(-1)^{|T|-1}} \&=prod_{varnothing subset Tsubseteq S} (gcd fib_{{T}})^{(-1)^{|T|-1}} end{aligned})
其中,( ext{lcm }fib_{{S}}) 表示集合 (S) 内所有 (a_i) 的斐波那契值的最小公倍数,即 (displaystyle ext{lcm}_{iin S} fib_{a_i})
同理,( ext{gcd }fib_{{S}}) 表示集合 (S) 内所有 (a_i) 的斐波那契值的最大公因数,即 (displaystyle ext{gcd}_{iin S} fib_{a_i})
而由于 (gcd(fib_a, fib_b)=fib_{gcd(a, b)}) ,故逐个展开得 ( ext{gcd }fib_{{T}}=fib_{gcd {T}})
因此 (displaystyle ext{lcm } fib_{{S}}=prod_{varnothing subset Tsubseteq S} fib_{gcd {T}}^{(-1)^{|T|-1}})
直接暴力枚举 (T) 显然复杂度为 (O(2^N)) 过不了 (Nleq 5 imes 10^4) 的数据
由于 (a_i) 的值域为 ([1, 10^6]) ,考虑莫比乌斯反演,枚举 (gcd {T})
(egin{aligned} ext{lcm } fib_{{S}}&=prod_{varnothing subset Tsubseteq S} fib_{gcd {T}}^{(-1)^{|T|-1}} \&=prod_{ggeq 1}left( prod_{varnothing subset Tsubseteq Swedge gcd{T}=g} fib_g^{(-1)^{|T|-1}} ight) \&=prod_{ggeq 1} fib_g^{displaystyle sum_{varnothing subset Tsubseteq S}[gcd {T}=g](-1)^{|T|-1}} end{aligned})
记 (displaystyle p(g)=sum_{varnothing subset Tsubseteq S}[gcd {T}=g](-1)^{|T|-1}, q(g)=sum_{varnothing subset Tsubseteq S}[gmid gcd {T}](-1)^{|T|-1})
则 (displaystyle ext{lcm }fib_{{S}}=prod_{ggeq 1} fib_g^{p(g)}, q(g)=sum_{gmid m}p(m))
因此,当我们求出 (q(g)) ,再反演出 (p(g)) 后,直接对上式跑快速幂求积,即可得出答案
而对于 (q(g)) ,若我们用 (cnt_g) 表示 (g) 即其倍数的个数,则有:
(displaystyle q(g)=sum_{i=1}^{cnt_g}inom{cnt_g} i cdot (-1)^{i-1}=inom{cnt_g} 0 - [cnt_g=0]=[cnt_g>0])
故求解方法显然:
先记录每个数字的出现次数,然后用迪利克雷前缀和 (O(nlog log n)) 跑出 (cnt_g)
之后将有值的 (cnt_g) 置 (1) ,否则置 (0) ,即 (O(n)) 得到 (q(g))
然后再用迪利克雷后缀差分 (O(nloglog n)) 跑出 (p(g))
因为 (q(g)) 是 (p(g)) 进行迪利克雷后缀和的结果
最后一边 (O(n)) 枚举斐波那契数,一边算 (fib_g^{p(g)}) 的结果即可
可将 (p(g)) 大于 (0) 与小于 (0) 的答案先分别存储,最后再对小于 (0) 的跑逆元
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
typedef double db;
#define fi first
#define se second
const int Lim=1e6, MAXN=Lim+10, P=1000000007;
inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%P) if(x&1) ans=ans*a%P; return ans; }
int n, cnt[MAXN];
bool nprime[MAXN];
inline void sieve() {
for(int i=2; i<=Lim; ++i) if(!nprime[i]) {
for(int j=i+i; j<=Lim; j+=i)
nprime[j]=1;
}
}
inline void init() {
cin>>n;
for(int i=1, a; i<=n; ++i) {
cin>>a;
++cnt[a];
}
for(int i=2; i<=Lim; ++i) if(!nprime[i]) {
for(int j=Lim/i, k=j*i; j; --j, k-=i)
cnt[j]+=cnt[k];
}
for(int i=1; i<=Lim; ++i) cnt[i]=(cnt[i]>0);
for(int i=2; i<=Lim; ++i) if(!nprime[i]) {
for(int j=1, k=i, J=Lim/i; j<=J; ++j, k+=i)
cnt[j]-=cnt[k];
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
sieve();
init();
int p1=1, p2=1, fib[3]={0, 1, 1};
for(int g=2, x; g<=Lim; ++g) {
x = fib[g%3] = (fib[(g+1)%3]+fib[(g+2)%3])%P;
if(cnt[g]>0) p1=(ll)p1*fpow(x, cnt[g])%P;
else if(cnt[g]<0) p2=(ll)p2*fpow(x, -cnt[g])%P;
}
cout<<(ll)p1*fpow(p2, P-2)%P;
cout.flush();
return 0;
}