【题解】Luogu-P5518 MtOI 2019 幽灵乐团

Page Views Count

莫比乌斯反演练习题

lcm 拆成 gcd,这样实际上要求的就是两个部分,分别是:

i=1Aj=1Bk=1Cif(type)

以及

i=1Aj=1Bk=1Cgcd(i,j)f(type)

注意第二个部分要取倒数。

type=0#

第一个部分就是 (A!)BC,计算第二个部分。

先枚举 gcd 的值,简单推导:

i=1Aj=1Bgcd(i,j)C=l=1AlCi=1Aj=1B[gcd(i,j)=l]=l=1AlCi=1A/lj=1B/ldgcd(i,j)μ(d)=l=1AlCd=1A/lμ(d)×A/ld×B/ld=T=1AlTlμ(T/l)×A/T×B/T×C

预处理出 f(T)=lTlμ(T/l) 就可以数论分块 O(nlogp) 解决。

可以讨论一下如何快速预处理出 f,可以写成 f(T)=lT(Tl)μ(l),而分母提出是 TlTμ(l)=1

接下来考虑分子,设 T=pT,pPp,容易发现二者值相同,那么对于每个质数单独考虑,其作为底数出现的所有项中,次数和应当是 μ(p)μ(lp),和式的结果当且仅当 l=p 时为 1 否则为 0,于是当 T 只含一个质因子 p 时,f(T)=p,反之为 f(T)=1

预处理的复杂度是 O(n)

type=1#

第一部分:

i=1Aj=1Bk=1Ciijk=(i=1Aii)B(B+1)2×C(C+1)2

第二部分也是比较套路的:

i=1Aj=1Bk=1Cgcd(i,j)ijk=i=1Aj=1Bgcd(i,j)ij×C(C+1)2=l=1Ali=1Aj=1Bij×C(C+1)2[gcd(i,j)=l]=l=1Ald=1A/lμ(d)×(ld)2×A/ld(A/ld+1)2×B/ld(B/ld+1)2×C(C+1)2=T=1AlTlμ(T/l)×T2×A/T(A/T+1)2×B/T(B/T+1)2×C(C+1)2

这部分只能 O(nlogn) 预处理 g(T)=lTlμ(T/l)×T2

单词询问依旧是数论分块。

type=2#

第一部分也不好算。

常规的式子写出是:

i=1Aililj=1Bk=1C[gcd(i,j,k)=l]

这个 li 的限制不是很友好,可以尝试枚举 l 的倍数:

l=1Ai=1A/l(il)lj=1B/lk=1C/l[gcd(i,j,k)=1]=l=1Ai=1A/l(il)lj=1B/lk=1C/ldgcd(i,j,k)μ(d)

这里 d 有一个 di 的限制,不如把 d 提在前面,然后枚举 i

l=1Ad=1A/li=1A/ld(ild)l×μ(d)×B/ld×C/ld=T=1AlTi=1A/T(iT)l×μ(T/l)×B/T×C/T=T=1AlT(AT!×TA/T)l×μ(T/l)×B/T×C/T

这个时候底数和 l 无关了,指数是 lTl×μ(Tl)×BT×CT,注意到 μId=φ,所以可以化成:

T=1A(AT!×TA/T)φ(T)×B/T×C/T

预处理 φ 的前缀和以及 Tφ(T) 的前缀积即可。

第二部分可以枚举 gcd(i,j)gcd(i,j,k)

l=1Ai=1Aj=1Bgcd(i,j)lk=1C[gcd(i,j,k)=l]=l=1Ai=1A/lj=1B/l(gcd(i,j)×l)lk=1C/l[gcd(i,j,k)=1]=l=1Ad=1A/li=1A/ldj=1B/ld(gcd(i,j)×ld)l×μ(d)×C/ld=T=1AlTi=1A/Tj=1B/T(gcd(i,j)×T)l×μ(T/l)×C/T=T=1Ai=1A/Tj=1B/T(gcd(i,j)×T)φ(T)×C/T

底数拆成两部分,T 的一部分是 T=1ATφ(T)×A/T×B/T×C/T,正常分块即可。

gcd(i,j) 的部分要再推一下,常规处理就可以:

i=1A/Tj=1B/Tgcd(i,j)=T=1A/TlTlμ(T/l)×A/TT×B/TT

还是上面的 f(T) 函数,注意到要对 T 做一次数论分块,内部要对 T 再做一次,两次数论分块的复杂度是 O(n3/4)

复杂度分析同一次分块类似,设第一次枚举到的值为 i,那么第二次分块的复杂度是 O(ni)

i>n 时,有 O(n) 个块,每个块大小不超过 n,所以复杂度 O(n3/4)

in 时,可以列出式子:

O(i=1nni)=O(i=1nni)=O(n1n1xdx)

1x2x 的导数,所以定积分是 2(n1/41),这样分析到了 O(n3/4) 的复杂度。

于是整体复杂度就是 O(nlogp+Tn3/4logp)

点击查看代码
inline int q_pow(int A,int B,int P){
    int res=1;
    while(B){
        if(B&1) res=1ll*res*A%P;
        A=1ll*A*A%P;
        B>>=1;
    }
    return res;
}

int T;
int mod,inv2;
int fact[maxn],prodpw[maxn];
int pr[maxn],mn[maxn],mu[maxn],sumphi[maxn],prodpwphi[maxn];
int f1[maxn],invf1[maxn],f2[maxn],pw[maxn],inv_pw[maxn],f3[maxn];
bool vis[maxn];

inline void linear_sieve(){
    mu[1]=1,sumphi[1]=1,f1[1]=1;
    for(int i=2;i<=lim;++i){
        if(!vis[i]) pr[++pr[0]]=i,mn[i]=i,mu[i]=-1,sumphi[i]=i-1,f1[i]=i;
        for(int j=1;j<=pr[0]&&i*pr[j]<=lim;++j){
            vis[i*pr[j]]=1,mn[i*pr[j]]=pr[j],mu[i*pr[j]]=-mu[i];
            if(mn[i]==pr[j]) sumphi[i*pr[j]]=sumphi[i]*pr[j],f1[i*pr[j]]=f1[i];
            else sumphi[i*pr[j]]=sumphi[i]*sumphi[pr[j]],f1[i*pr[j]]=1;
            if(i%pr[j]==0){
                mu[i*pr[j]]=0;
                break;
            }
        }
    }
    prodpwphi[0]=1;
    for(int i=1;i<=lim;++i) prodpwphi[i]=1ll*prodpwphi[i-1]*q_pow(i,sumphi[i],mod)%mod;
    for(int i=1;i<=lim;++i) sumphi[i]=(sumphi[i-1]+sumphi[i])%(mod-1);
    for(int i=1;i<=lim;++i) f2[i]=q_pow(f1[i],1ll*i*i%(mod-1),mod);
    f1[0]=1;
    for(int i=1;i<=lim;++i) f1[i]=1ll*f1[i-1]*f1[i]%mod;
    for(int i=0;i<=lim;++i) invf1[i]=q_pow(f1[i],mod-2,mod);
    f2[0]=1;
    for(int i=1;i<=lim;++i) f2[i]=1ll*f2[i-1]*f2[i]%mod;
    for(int i=1;i<=lim;++i){
        pw[i]=q_pow(i,i,mod),inv_pw[i]=q_pow(pw[i],mod-2,mod);
        f3[i]=1;
    }
    for(int i=1;i<=lim;++i){
        for(int j=1;i*j<=lim;++j){
            if(mu[j]==1) f3[i*j]=1ll*f3[i*j]*pw[i]%mod;
            if(mu[j]==-1) f3[i*j]=1ll*f3[i*j]*inv_pw[i]%mod;
        }
    }
    f3[0]=1;
    for(int i=1;i<=lim;++i) f3[i]=1ll*f3[i-1]*f3[i]%mod;
    fact[0]=1;
    for(int i=1;i<=lim;++i) fact[i]=1ll*fact[i-1]*i%mod;
    prodpw[0]=1;
    for(int i=1;i<=lim;++i) prodpw[i]=1ll*prodpw[i-1]*q_pow(i,i,mod)%mod;
}

inline int calc1(int A,int B,int C){
    return q_pow(fact[A],1ll*B*C%(mod-1),mod);
}
inline int calc2(int A,int B,int C){
    int res=1;
    if(A>B) swap(A,B);
    for(int l=1,r;l<=A;l=r+1){
        r=min(A/(A/l),B/(B/l));
        int now=1ll*f1[r]*invf1[l-1]%mod;
        now=q_pow(now,1ll*C*(A/l)%(mod-1)*(B/l)%(mod-1),mod);
        res=1ll*res*now%mod;
    } 
    res=q_pow(res,mod-2,mod);
    return res;
}
inline void solve0(int A,int B,int C){
    int res;
    res=1ll*calc1(A,B,C)*calc1(B,A,C)%mod;
    res=1ll*res*calc2(A,B,C)%mod*calc2(A,C,B)%mod;
    printf("%d ",res);
}

inline int calc3(int A,int B,int C){
    int e1=1ll*B*(B+1)/2%(mod-1),e2=1ll*C*(C+1)/2%(mod-1);
    return q_pow(prodpw[A],1ll*e1*e2%(mod-1),mod);
}
inline int calc4(int A,int B,int C){
    int res=1;
    if(A>B) swap(A,B);
    for(int l=1,r;l<=A;l=r+1){
        r=min(A/(A/l),B/(B/l));
        int now=1ll*f2[r]*q_pow(f2[l-1],mod-2,mod)%mod;
        int e1=1ll*(A/l)*(A/l+1)/2%(mod-1),e2=1ll*(B/l)*(B/l+1)/2%(mod-1),e3=1ll*C*(C+1)/2%(mod-1);
        now=q_pow(now,1ll*e1*e2%(mod-1)*e3%(mod-1),mod);
        res=1ll*res*now%mod;
    }
    res=q_pow(res,mod-2,mod);
    return res;
}
inline void solve1(int A,int B,int C){
    int res;
    res=1ll*calc3(A,B,C)*calc3(B,A,C)%mod;
    res=1ll*res*calc4(A,B,C)%mod*calc4(A,C,B)%mod;
    printf("%d ",res);
}

inline int calc5(int A,int B,int C){
    int res=1;
    for(int l=1,r;l<=min({A,B,C});l=r+1){
        r=min({A/(A/l),B/(B/l),C/(C/l)});
        int now=q_pow(fact[A/l],(sumphi[r]-sumphi[l-1]+mod-1)%(mod-1),mod);
        now=1ll*now*q_pow(1ll*prodpwphi[r]*q_pow(prodpwphi[l-1],mod-2,mod)%mod,A/l,mod)%mod;
        now=q_pow(now,1ll*(B/l)*(C/l)%(mod-1),mod)%mod;
        res=1ll*res*now%mod;
    }
    return res;
}
inline int calc6(int A,int B,int C){
    int res=1;
    if(A>B) swap(A,B);
    for(int l=1,r;l<=min({A,B,C});l=r+1){
        r=min({A/(A/l),B/(B/l),C/(C/l)});
        int now=1ll*prodpwphi[r]*q_pow(prodpwphi[l-1],mod-2,mod)%mod;
        now=q_pow(now,1ll*(A/l)*(B/l)%(mod-1)*(C/l)%(mod-1),mod);
        res=1ll*res*now%mod;
    }
    int cnt=1;
    for(int l1=1,r1;l1<=min({A,B,C});l1=r1+1){
        r1=min({A/(A/l1),B/(B/l1),C/(C/l1)});
        int now1=1;
        for(int l2=1,r2;l2<=min(A/l1,B/l1);l2=r2+1){
            ++cnt;
            r2=min((A/l1)/(A/l1/l2),(B/l1)/(B/l1/l2));
            int now2=1ll*f1[r2]*invf1[l2-1]%mod;
            now2=q_pow(now2,1ll*(A/l1/l2)*(B/l1/l2)%(mod-1),mod);
            now1=1ll*now1*now2%mod;
        }
        now1=q_pow(now1,1ll*(sumphi[r1]-sumphi[l1-1]+mod-1)%(mod-1)*(C/l1)%(mod-1),mod);
        res=1ll*res*now1%mod;
    }
    res=q_pow(res,mod-2,mod);
    cerr<<cnt<<endl;
    return res;
}
inline void solve2(int A,int B,int C){
    int res=1;
    res=1ll*calc5(A,B,C)*calc5(B,A,C)%mod;
    res=1ll*res*calc6(A,B,C)%mod*calc6(A,C,B)%mod;
    printf("%d\n",res);
}

int A,B,C;

int main(){
    T=read(),mod=read();
    linear_sieve();
    while(T--){
        A=read(),B=read(),C=read();
        solve0(A,B,C);
        solve1(A,B,C);
        solve2(A,B,C);
    }
    return 0;
}

作者:SoyTony

出处:https://www.cnblogs.com/SoyTony/p/Solution_on_Luogu-P5518.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   SoyTony  阅读(66)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示