【题解】Luogu-P5518 MtOI 2019 幽灵乐团
莫比乌斯反演练习题
把 \(\mathrm{lcm}\) 拆成 \(\gcd\),这样实际上要求的就是两个部分,分别是:
以及
注意第二个部分要取倒数。
\(type=0\)
第一个部分就是 \((A!)^{BC}\),计算第二个部分。
先枚举 \(\gcd\) 的值,简单推导:
预处理出 \(f(T)=\prod_{l\mid T} l^{\mu(T/l)}\) 就可以数论分块 \(O(\sqrt{n}\log p)\) 解决。
可以讨论一下如何快速预处理出 \(f\),可以写成 \(f(T)=\prod_{l\mid T} \left(\frac{T}{l}\right)^{\mu(l)}\),而分母提出是 \(T^{\sum_{l\mid T}\mu(l)}=1\)。
接下来考虑分子,设 \(T'=\prod_{p\mid T,p\in \mathbf{P}} p\),容易发现二者值相同,那么对于每个质数单独考虑,其作为底数出现的所有项中,次数和应当是 \(\mu(p)\sum \mu\left(\frac{l}{p}\right)\),和式的结果当且仅当 \(l=p\) 时为 \(1\) 否则为 \(0\),于是当 \(T\) 只含一个质因子 \(p\) 时,\(f(T)=p\),反之为 \(f(T)=1\)。
预处理的复杂度是 \(O(n)\)。
\(type=1\)
第一部分:
第二部分也是比较套路的:
这部分只能 \(O(n\log n)\) 预处理 \(g(T)=\prod_{l\mid T}l^{\mu(T/l)\times T^2}\)。
单词询问依旧是数论分块。
\(type=2\)
第一部分也不好算。
常规的式子写出是:
这个 \(l\mid i\) 的限制不是很友好,可以尝试枚举 \(l\) 的倍数:
这里 \(d\) 有一个 \(d\mid i\) 的限制,不如把 \(d\) 提在前面,然后枚举 \(i\):
这个时候底数和 \(l\) 无关了,指数是 \(\sum_{l\mid T} l\times \mu\left(\frac{T}{l}\right)\times \left\lfloor \frac{B}{T}\right\rfloor\times \left\lfloor \frac{C}{T}\right\rfloor\),注意到 \(\mu * \mathrm{Id}=\varphi\),所以可以化成:
预处理 \(\varphi\) 的前缀和以及 \(T^{\varphi(T)}\) 的前缀积即可。
第二部分可以枚举 \(\gcd(i,j)\) 或 \(\gcd(i,j,k)\)。
底数拆成两部分,\(T\) 的一部分是 \(\prod_{T=1}^{A} T^{\varphi(T)\times \left\lfloor A/T\right\rfloor\times \left\lfloor B/T\right\rfloor\times \left\lfloor C/T\right\rfloor}\),正常分块即可。
\(\gcd(i,j)\) 的部分要再推一下,常规处理就可以:
还是上面的 \(f(T)\) 函数,注意到要对 \(T\) 做一次数论分块,内部要对 \(T'\) 再做一次,两次数论分块的复杂度是 \(O(n^{3/4})\)。
复杂度分析同一次分块类似,设第一次枚举到的值为 \(i\),那么第二次分块的复杂度是 \(O\left(\sqrt{\left\lfloor\frac{n}{i}\right\rfloor}\right)\)。
当 \(i>\sqrt{n}\) 时,有 \(O(\sqrt{n})\) 个块,每个块大小不超过 \(\sqrt{n}\),所以复杂度 \(O(n^{3/4})\)。
当 \(i\le \sqrt{n}\) 时,可以列出式子:
而 \(\frac{1}{\sqrt{x}}\) 是 \(2\sqrt{x}\) 的导数,所以定积分是 \(2(n^{1/4}-1)\),这样分析到了 \(O(n^{3/4})\) 的复杂度。
于是整体复杂度就是 \(O(n\log p+Tn^{3/4}\log p)\)。
点击查看代码
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;
}