luoguP5518 [MtOI2019]幽灵乐团 / 莫比乌斯反演基础练习题

一道毒瘤题目,题目链接:luoguP5518

题意

题目大意:求 i=1Aj=1Bk=1C(lcm(i,j)gcd(i,k))f(type)

其中 type {0,1,2}f(0)=1,f(1)=ijk,f(2)=gcd(i,j,k)

题解

让我们先来颓一下柿子。

i=1Aj=1Bk=1C(lcm(i,j)gcd(i,k))f(type)=i=1Aj=1Bk=1C(ijgcd(i,j)gcd(i,k))f(type)lcm(i,j)=ijgcd(i,j)=i=1Aj=1Bk=1C(ijgcd(i,j)gcd(i,k))f(type)=i=1Aj=1Bk=1Cif(type)jf(type)gcd(i,j)f(type)gcd(i,k)f(type)=(i=1Aj=1Bk=1Cif(type))×(i=1Aj=1Bk=1Cjf(type))(i=1Aj=1Bk=1Cgcd(i,j)f(type))×(i=1Aj=1Bk=1Cgcd(i,k)f(type))

实际上只需要知道 i=1Aj=1Bk=1Cif(type)i=1Aj=1Bk=1Cgcd(i,j)f(type) 的求法就可以了。

所以我们一共要推 6 个式子。

type=0

i=1Aj=1Bk=1Ci=i=1AiBCi

i=1Aj=1Bk=1Cgcd(i,j)=i=1Aj=1Bgcd(i,j)C=pprime,m0i=1Aj=1BpC[pm|gcd(i,j)]gcd,gcd=pprime,m0pApmBpmC

第一个式子直接预处理阶乘,然后算 BC 次方。

第二个式子可以通过预处理筛出 pm ,然后快速幂。

如果想要更快可以筛出所有 pm 的前缀积,然后数论分块。

type=1

i=1Aj=1Bk=1Ciijk=i=1Aiij=1Bk=1Cjk=i=1Aii(j=1Bj)(k=1Ck)=i=1Aiif(B)f(C)f(n)=n(n+1)2

i=1Aj=1Bk=1Cgcd(i,j)ijk=i=1Aj=1Bgcd(i,j)ijk=1Ck=i=1Aj=1Bgcd(i,j)ijf(C)=pprime,m0i=1Aj=1Bp[pm|gcd(i,j)]ijf(C)=pprime,m0pp2mf(Apm)f(Bpm)f(C)

第二个式子计算方式和type=0差不多,第一个式子只需要预处理 ii 的前缀积即可。

type=2

i=1Aj=1Bk=1Cigcd(i,j,k)=d=1Di=1Aij=1Bk=1Cd[gcd(i,j,k=d)]D=min(A,B,C),gcd=d=1Di=1Adij=1Bdk=1Cdd[gcd(i,j,k=1)]=d=1Di=1Ad(id)j=1Bdk=1Cddt|gcd(i,j,k)μ(t)=d=1Dt=1Ddi=1Adt(idt)dμ(t)BdtCdtt=T=1Di=1AT(iT)BTCTd|Tdμ(Td)T=dt=T=1Di=1AT(iT)BTCTφ(T)idμ=φ=(T=1D(AT!)BTCTφ(T))×(T=1DTφ(T)ATBTCT)

i=1Aj=1Bk=1Cgcd(i,j)gcd(i,j,k)=d=1Di=1Adj=1Bd(gcd(i,j)d)dk=1C[gcd(i,j,k)=1]gcd(i,j,k)=d=1Di=1Adj=1Bd(gcd(i,j)d)dk=1Ct|gcd(i,j,k)μ(t)=d=1Dt=1Ddi=1Atdj=1Btd(gcd(i,j)td)dμ(t)Ctd=T=1Di=1ATj=1BT(gcd(i,j)T)CTd|Tdμ(Td)=T=1Di=1ATj=1BT(gcd(i,j)T)CTφ(T)idμ=φ=(T=1DTφ(T)ATBTCT)×(T=1Di=1ATj=1BTgcd(i,j)φ(T)CT)

然后你就会发现分子的右边的式子和分母的左边的式子约掉了。

于是我们现在只需要求 T=1D(AT!)BTCTφ(T)T=1Di=1ATj=1BTgcd(i,j)φ(T)CT

第一个式子只需要预处理阶乘、 φ 的前缀和,然后就可以分块。

第二个式子还需要推一推。

T=1Di=1ATj=1BTgcd(i,j)φ(T)CT=T=1D(d=1ETdi=1ATdj=1BTdt|gcd(i,j)μ(t))φ(T)CTE=min(A,B),gcd=T=1D(d=1ETt=1ETddμ(t)ATtdBTtd)φ(T)CTT=1D(T=1ET(d|Tdμ(Td))ATTBTT)φ(T)CT

然后你会发现 T=1n(d|Tdμ(Td)) 是可以预处理的。

于是乎,两遍整除分块就可以了。

最终时间复杂度是 O(nlogn+Tn34logn) 的。

代码:

#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
using ll=long long;
inline int read()
{
    int s=0,w=1;char ch;
    while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
}
int mod;
ll qow(ll a,int b)
{
    b=(b%(mod-1)+mod-1)%(mod-1);
    a%=mod;
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
map<int,ll>jyh[100001];
ll prephi[100001];
int pri[100001];
bool is[100001];
int phi[100001];
int pm[100001];
int mu[100001];
ll jc1[100001];
ll jc2[100001];
ll d1[100001];
ll d2[100001];
int A,B,C;
inline ll f(int i){ return 1ll*i*(i+1)/2%(mod-1);}
inline ll get2(int A,int B)
{
    ll ans=jyh[A][B];
    if(ans) return ans;
    ans=1;
    int E=min(A,B);
    for(int lTp=1,rTp;lTp<=E;lTp=rTp+1)
    {
        rTp=min(A/(A/lTp),B/(B/lTp));
        ll val=d1[rTp]*d2[lTp-1]%mod;
        ans=ans*qow(val,(ll)(A/lTp)*(B/rTp)%(mod-1))%mod;
    }
    return jyh[A][B]=ans;
}
inline ll get1(int A,int B,int C)
{
    ll ans=1;
    int D=min(min(A,B),C);
    for(int lT=1,rT;lT<=D;lT=rT+1)
    {
        rT=min(min(A/(A/lT),B/(B/lT)),C/(C/lT));
        ans=ans*qow(get2(A/lT,B/lT),(prephi[rT]-prephi[lT-1])%(mod-1)*(C/lT)%(mod-1))%mod
               *qow(get2(A/lT,C/lT),(prephi[rT]-prephi[lT-1])%(mod-1)*(B/lT)%(mod-1))%mod;
    }
    return ans;
}
inline ll get3(int A,int B,int C)
{
    ll ans=1;
    for(int lT=1,rT;lT<=A&&lT<=B&&lT<=C;lT=rT+1)
    {
        rT=min(min(A/(A/lT),B/(B/lT)),C/(C/lT));
        ans=ans*qow(jc1[A/lT],(prephi[rT]-prephi[lT-1])%(mod-1)*(B/lT)*(C/lT)%(mod-1))%mod;
    }
    return ans;
}
int main()
{
    int t=read(),n=100000;mod=read();
    mu[1]=phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!is[i]) pri[++pri[0]]=i,mu[i]=-1,phi[i]=i-1;
        for(int j=1;j<=pri[0]&&i*pri[j]<=n;j++)
        {
            is[i*pri[j]]=1;
            if(i%pri[j]==0)
            {
                phi[i*pri[j]]=phi[i]*pri[j];
                break;
            }
            mu[i*pri[j]]=-mu[i];
            phi[i*pri[j]]=phi[i]*(pri[j]-1);
        }
    }
    for(int i=1;i<=pri[0];i++) for(ll j=pri[i];j<=n;j*=pri[i]) pm[j]=pri[i];
    jc1[0]=jc2[0]=d1[0]=d2[0]=1;
    for(int i=1;i<=n;i++)
    {
        d1[i]=1;
        prephi[i]=prephi[i-1]+phi[i];
        jc1[i]=jc1[i-1]*i%mod;
        jc2[i]=jc2[i-1]*qow(i,i)%mod;
    }
    for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i)
        d1[j]=d1[j]*qow(i,mu[j/i])%mod;
    for(int i=2;i<=n;i++) d1[i]=(d1[i]*d1[i-1])%mod;
    for(int i=1;i<=n;i++) d2[i]=qow(d1[i],mod-2);
    while(t--)
    {
        A=read(),B=read(),C=read();
        ll ans=1,val=1;



        ans=ans*qow(jc1[A],1ll*B*C%(mod-1))%mod;
        ans=ans*qow(jc1[B],1ll*A*C%(mod-1))%mod;
        for(int i=1;i<=A&&i<=B;i++) if(pm[i])
            val=val*qow(pm[i],1ll*(A/i)*(B/i)%(mod-1))%mod;
        val=qow(val,C);
        ans=ans*qow(val,mod-2)%mod,val=1;
        for(int i=1;i<=A&&i<=C;i++) if(pm[i])
            val=val*qow(pm[i],1ll*(A/i)*(C/i)%(mod-1))%mod;
        val=qow(val,B);
        ans=ans*qow(val,mod-2)%mod,val=1;
        printf("%lld ",ans),ans=1;



        ans=ans*qow(jc2[A],f(B)*f(C)%(mod-1))%mod;
        ans=ans*qow(jc2[B],f(A)*f(C)%(mod-1))%mod;
        for(int i=1;i<=A&&i<=B;i++) if(pm[i])
            val=val*qow(pm[i],f(A/i)*f(B/i)%(mod-1)*i%(mod-1)*i%(mod-1))%mod;
        val=qow(val,f(C));
        ans=ans*qow(val,mod-2)%mod,val=1;
        for(int i=1;i<=A&&i<=C;i++) if(pm[i])
            val=val*qow(pm[i],f(A/i)*f(C/i)%(mod-1)*i%(mod-1)*i%(mod-1))%mod;
        val=qow(val,f(B));
        ans=ans*qow(val,mod-2)%mod,val=1;
        printf("%lld ",ans),ans=1;



        ans=qow(get1(A,B,C),mod-2);
        ans=ans*get3(A,B,C)%mod*get3(B,A,C)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}
posted @   Wuyanru  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示