题解 loj 6102 斐波那契的最小公倍数

传送门-LOJ

传送门-51Nod


【分析】

对每个质因数 \(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;
}
posted @ 2021-09-02 20:23  JustinRochester  阅读(55)  评论(0编辑  收藏  举报