题解 loj 6102 斐波那契的最小公倍数
【分析】
对每个质因数 \(p_i\) 进行 min-max 容斥得:
\(\displaystyle \max_{p_i}(S)=\sum_{\varnothing\subset T\subseteq S}(-1)^{|T|-1}\min_{p_i}(T)\)
故 \(\displaystyle p_i^{\displaystyle \max_{p_i}(S)}=p_i^{\displaystyle \sum_{\varnothing\subset T\subseteq S}(-1)^{|T|-1}\min_{p_i}(T)}\)
对所有 \(p_i\) 累乘得:
\(\begin{aligned} \text{lcm } fib_{\{S\}}&=\prod_i p_i^{\displaystyle \max_{p_i}(T)} \\&=\prod_i p_i^{\displaystyle \sum_{\varnothing\subset T\subseteq S}(-1)^{|T|-1}\min_{p_i}(T)} \\&=\prod_{\varnothing \subset T\subseteq S} (\prod_i p_i^{\displaystyle \min_{p_i}(T)})^{(-1)^{|T|-1}} \\&=\prod_{\varnothing \subset T\subseteq S} (\gcd fib_{\{T\}})^{(-1)^{|T|-1}} \end{aligned}\)
其中,\(\text{lcm }fib_{\{S\}}\) 表示集合 \(S\) 内所有 \(a_i\) 的斐波那契值的最小公倍数,即 \(\displaystyle \text{lcm}_{i\in S}\ fib_{a_i}\)
同理,\(\text{gcd }fib_{\{S\}}\) 表示集合 \(S\) 内所有 \(a_i\) 的斐波那契值的最大公因数,即 \(\displaystyle \text{gcd}_{i\in S}\ fib_{a_i}\)
而由于 \(\gcd(fib_a, fib_b)=fib_{\gcd(a, b)}\) ,故逐个展开得 \(\text{gcd }fib_{\{T\}}=fib_{\gcd \{T\}}\)
因此 \(\displaystyle \text{lcm } fib_{\{S\}}=\prod_{\varnothing \subset T\subseteq S} fib_{\gcd \{T\}}^{(-1)^{|T|-1}}\)
直接暴力枚举 \(T\) 显然复杂度为 \(O(2^N)\) 过不了 \(N\leq 5\times 10^4\) 的数据
由于 \(a_i\) 的值域为 \([1, 10^6]\) ,考虑莫比乌斯反演,枚举 \(\gcd \{T\}\)
\(\begin{aligned} \text{lcm } fib_{\{S\}}&=\prod_{\varnothing \subset T\subseteq S} fib_{\gcd \{T\}}^{(-1)^{|T|-1}} \\&=\prod_{g\geq 1}\left( \prod_{\varnothing \subset T\subseteq S\wedge \gcd\{T\}=g} fib_g^{(-1)^{|T|-1}}\right) \\&=\prod_{g\geq 1} fib_g^{\displaystyle \sum_{\varnothing \subset T\subseteq S}[\gcd \{T\}=g](-1)^{|T|-1}} \end{aligned}\)
记 \(\displaystyle p(g)=\sum_{\varnothing \subset T\subseteq S}[\gcd \{T\}=g](-1)^{|T|-1}, q(g)=\sum_{\varnothing \subset T\subseteq S}[g\mid \gcd \{T\}](-1)^{|T|-1}\)
则 \(\displaystyle \text{lcm }fib_{\{S\}}=\prod_{g\geq 1} fib_g^{p(g)}, q(g)=\sum_{g\mid m}p(m)\)
因此,当我们求出 \(q(g)\) ,再反演出 \(p(g)\) 后,直接对上式跑快速幂求积,即可得出答案
而对于 \(q(g)\) ,若我们用 \(cnt_g\) 表示 \(g\) 即其倍数的个数,则有:
\(\displaystyle q(g)=\sum_{i=1}^{cnt_g}\binom{cnt_g} i \cdot (-1)^{i-1}=\binom{cnt_g} 0 - [cnt_g=0]=[cnt_g>0]\)
故求解方法显然:
先记录每个数字的出现次数,然后用迪利克雷前缀和 \(O(n\log \log n)\) 跑出 \(cnt_g\)
之后将有值的 \(cnt_g\) 置 \(1\) ,否则置 \(0\) ,即 \(O(n)\) 得到 \(q(g)\)
然后再用迪利克雷后缀差分 \(O(n\log\log 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;
}