【题解】Luogu-P5518 MtOI 2019 幽灵乐团
莫比乌斯反演练习题
把 拆成 ,这样实际上要求的就是两个部分,分别是:
以及
注意第二个部分要取倒数。
#
第一个部分就是 ,计算第二个部分。
先枚举 的值,简单推导:
预处理出 就可以数论分块 解决。
可以讨论一下如何快速预处理出 ,可以写成 ,而分母提出是 。
接下来考虑分子,设 ,容易发现二者值相同,那么对于每个质数单独考虑,其作为底数出现的所有项中,次数和应当是 ,和式的结果当且仅当 时为 否则为 ,于是当 只含一个质因子 时,,反之为 。
预处理的复杂度是 。
#
第一部分:
第二部分也是比较套路的:
这部分只能 预处理 。
单词询问依旧是数论分块。
#
第一部分也不好算。
常规的式子写出是:
这个 的限制不是很友好,可以尝试枚举 的倍数:
这里 有一个 的限制,不如把 提在前面,然后枚举 :
这个时候底数和 无关了,指数是 ,注意到 ,所以可以化成:
预处理 的前缀和以及 的前缀积即可。
第二部分可以枚举 或 。
底数拆成两部分, 的一部分是 ,正常分块即可。
的部分要再推一下,常规处理就可以:
还是上面的 函数,注意到要对 做一次数论分块,内部要对 再做一次,两次数论分块的复杂度是 。
复杂度分析同一次分块类似,设第一次枚举到的值为 ,那么第二次分块的复杂度是 。
当 时,有 个块,每个块大小不超过 ,所以复杂度 。
当 时,可以列出式子:
而 是 的导数,所以定积分是 ,这样分析到了 的复杂度。
于是整体复杂度就是 。
点击查看代码
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 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效