P5518 [MtOI2019]幽灵乐团 / 莫比乌斯反演基础练习题 题解
怎么说呢,这题确实算是基础练习题。就是强行六合一确实很顶。
\(T\) 组询问,每次给出 \(A,B,C\),对于 \(type=0,1,2\),分别求:
\[\prod_{i=1}^A\prod_{j=1}^B\prod_{k=1}^C\left(\dfrac{\mathrm{lcm}(i,j)}{\gcd(i,k)}\right)^{f(type)} \]其中:
\[f(0)=1,f(1)=ijk,f(2)=\gcd(i,j,k) \]答案对给定质数 \(p\) 取模。(\(1\le A,B,C\le10^5,10^7\le p\le 1.05\times10^9,T=70,p\) 是质数)
首先先简单化简一下所给式子:
这样我们的问题就转化为了求下面两个式子的值:
原式的其他部分都可以通过简单交换 \(A,B\) 或 \(B,C\) 转化成上式。接下来我们就分别对 \(type=0,1,2\) 推一推式子。
\(type=0\) 是比较简单的一种情况。对于第一个式子我们有:
而对于第二个式子,我们考虑比较套路的枚举 \(\gcd\),下文中记 \(f(t)=\prod_{d|t}d^{\mu\left(\frac{t}{d}\right)}\):
现在可以用类似预处理狄利克雷卷积的方法在 \(\mathcal{O}(n\log n)\) 的时间内预处理出 \(f\) 的前缀积,然后就可以关于 \(t\) 整除分块单次 \(\mathcal{O}(\sqrt{n}\log n)\) 求解。
综上,我们想处理 \(type=0\) 的情况,需要额外预处理的是 \(\mu\),\(f\),\(f\) 的前缀积和阶乘。预处理时间复杂度 \(\mathcal{O}(n\log n)\),单次询问 \(\mathcal{O}(\sqrt{n}\log n)\)。总式子:
然后是 \(type=1\),这种情况稍微复杂一点,但思维难度也不是很大。下文中记 \(S(n)=\dfrac{n(n+1)}{2},T(n)=\prod_{i=1}^ni^i\)。首先对于第一个式子:
而对于第二个式子,还是套路枚举 \(\gcd\),只不过比 \(type=0\) 的情况化简繁琐了一点,记 \(g(t)=f(t)^{t^2}\):
预处理出 \(f\) 就可以求出 \(g\) 和 \(g\) 的前缀积了,预处理 \(\mathcal{O}(n)\),然后关于 \(t\) 数论分块即可做到单次 \(\mathcal{O}(\sqrt{n}\log n^2)\) 求解。
综上,我们想求解 \(type=1\) 的情况,需要额外预处理的是 \(g\),\(g\) 的前缀积,\(T(n)\),预处理时间复杂度 \(\mathcal{O}(n)\) 单次询问 \(\mathcal{O}(\sqrt{n}\log n^2)\)。总式子:
最后是 \(type=2\) 的情况,这玩意会比上面的都复杂,而且需要一定的思维能力。对于第一个式子,我们之前处理 \(k\) 的时候都是直接把它提到指数变成求和,然后发现非常好处理,但这次好像就不太行了。不过看到 \(\gcd\) 我们还是先尝试枚举一下 \(\gcd\):
好像也算把 \(k\) 变成求和扔到指数了,那就顺着这个思路继续推吧:
总算推完了,其中用到了 \(\mu\ast \mathrm{id}=\varphi\) 的结论。最后一步把整个式子拆开成两个部分是伏笔,会在第二个式子的化简过程中回收。第二个式子就更麻烦了,我们面前有两个 \(\gcd\),该枚举哪个呢?我会两个都试一遍,这里省略尝试的过程,结论是枚举 \(\gcd(i,j,k)\) 是走得通的,大概是因为这个还是可以把 \(k\) 变成求和扔到指数:
啊,到这里这个式子就稍微有点复杂了,再强行往下化就有点麻烦了。我们考虑对于 \(dp,\gcd(i,j)\) 我们有完全两种化简思路,所以考虑把这俩拆开做。先处理 \(dp\):
好像有点熟悉?是的,这东西在分子上也出现了,而且分子分母上都出现了两次,可以约掉了!然后我们考虑 \(\gcd(i,j)\),思路还是枚举 \(\gcd\):
这里用到了这个结论:
然后就可以整除分块套整除分块解决了,时间复杂度 \(\mathcal{O}(n^{0.75}\log n)\)。整除分块套整除分块的复杂度:
综上,我们想要解决 \(type=2\) 的情况,需要额外预处理的是 \(\varphi\),\(\varphi\) 的前缀积。预处理时间复杂度 \(\mathcal{O}(n)\),单次询问时间复杂度 \(\mathcal{O}(n^{0.75}\log n)\)。总式子:
总结一下吧。我们要预处理的东西是 \(\mu\),\(f\),\(f\) 的前缀积,\(g\),\(g\) 的前缀和,\(T(n)\),阶乘,\(\varphi\),\(\varphi\) 的前缀积。
总时间复杂度 \(\mathcal{O}(n\log n+Tn^{0.75}\log n)\)。注意在指数上的东西要对 \(p-1\) 取模。
#include <cstdio>
#include <algorithm>
const int N = 1e5 + 10; typedef long long ll; int mod;
int f[N], sf[N], s[N], isf[N], mu[N], fT[N], f2[N], sf2[N], isf2[N], phi[N], sp[N], p[N], vis[N], fac[N], tp;
inline int ksm(int a, int b)
{
if (b < 0) return ksm(ksm(a, -b), mod - 2);
int ret = 1;
while (b)
{
if (b & 1) ret = (ll)ret * a % mod;
a = (ll)a * a % mod; b >>= 1;
}
return ret;
}
inline void init(int n)
{
mu[1] = phi[1] = fT[0] = fac[0] = sf[0] = isf[0] = sf2[0] = isf2[0] = 1;
for (int i = 2; i <= n; ++i)
{
if (!vis[i]) p[++tp] = i, mu[i] = -1, phi[i] = i - 1;
for (int j = 1; j <= tp && i * p[j] <= n; ++j)
{
vis[i * p[j]] = 1;
if (i % p[j] == 0) { mu[i * p[j]] = 0; phi[i * p[j]] = (ll)phi[i] * p[j] % (mod - 1); break; }
mu[i * p[j]] = -mu[i]; phi[i * p[j]] = (ll)phi[i] * phi[p[j]] % (mod - 1);
}
}
for (int i = 1; i <= n; ++i) f[i] = f2[i] = 1;
for (int i = 1; i <= n; ++i)
for (int j = i; j <= n; j += i) f[j] = (ll)f[j] * ksm(i, mu[j / i]) % mod;
for (int i = 1; i <= n; ++i) f2[i] = ksm(f[i], (ll)i * i % (mod - 1));
for (int i = 1; i <= n; ++i) fT[i] = (ll)fT[i - 1] * ksm(i, i) % mod;
for (int i = 1; i <= n; ++i) sp[i] = (sp[i - 1] + phi[i]) % (mod - 1);
for (int i = 1; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % mod;
for (int i = 1; i <= n; ++i) sf[i] = (ll)sf[i - 1] * f[i] % mod, sf2[i] = (ll)sf2[i - 1] * f2[i] % mod;
isf[n] = ksm(sf[n], mod - 2); isf2[n] = ksm(sf2[n], mod - 2);
for (int i = n - 1; i >= 1; --i) isf[i] = (ll)isf[i + 1] * f[i + 1] % mod, isf2[i] = (ll)isf2[i + 1] * f2[i + 1] % mod;
for (int i = 1; i <= n; ++i) s[i] = (ll)i * (i + 1) / 2 % (mod - 1);
}
inline int type0(int A, int B, int C)
{
int ans1, ans2;
ans1 = (ll)ksm(fac[A], (ll)B * C % (mod - 1)) * ksm(fac[B], (ll)A * C % (mod - 1)) % mod;
int n = std::min(A, B), t1 = 1, t2 = 1;
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), B / (B / l));
t1 = (ll)t1 * ksm((ll)sf[r] * isf[l - 1] % mod, (ll)(A / l) * (B / l) % (mod - 1)) % mod;
}
t1 = ksm(t1, C); n = std::min(A, C);
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), C / (C / l));
t2 = (ll)t2 * ksm((ll)sf[r] * isf[l - 1] % mod, (ll)(A / l) * (C / l) % (mod - 1)) % mod;
}
t2 = ksm(t2, B); ans2 = (ll)t1 * t2 % mod;
return (ll)ans1 * ksm(ans2, mod - 2) % mod;
}
inline int type1(int A, int B, int C)
{
int ans1, ans2;
ans1 = (ll)ksm(fT[A], (ll)s[B] * s[C] % (mod - 1)) * ksm(fT[B], (ll)s[A] * s[C] % (mod - 1)) % mod;
int n = std::min(A, B), t1 = 1, t2 = 1;
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), B / (B / l));
t1 = (ll)t1 * ksm((ll)sf2[r] * isf2[l - 1] % mod, (ll)s[A / l] * s[B / l] % (mod - 1)) % mod;
}
t1 = ksm(t1, s[C]); n = std::min(A, C);
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), C / (C / l));
t2 = (ll)t2 * ksm((ll)sf2[r] * isf2[l - 1] % mod, (ll)s[A / l] * s[C / l] % (mod - 1)) % mod;
}
t2 = ksm(t2, s[B]); ans2 = (ll)t1 * t2 % mod;
return (ll)ans1 * ksm(ans2, mod - 2) % mod;
}
inline int type2(int A, int B, int C)
{
int ans1, ans2;
int n = std::min(A, std::min(B, C)), m, t1 = 1, t2 = 1;
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
t1 = (ll)t1 * ksm(fac[A / l], (ll)(B / l) * (C / l) % (mod - 1) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod;
}
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
t2 = (ll)t2 * ksm(fac[B / l], (ll)(A / l) * (C / l) % (mod - 1) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod;
}
ans1 = (ll)t1 * t2 % mod; t1 = t2 = 1; int mul, tA, tB, tC;
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
m = std::min(A / l, B / l); mul = 1; tA = A / l; tB = B / l;
for (int i = 1, j; i <= m; i = j + 1)
{
j = std::min(tA / (tA / i), tB / (tB / i));
mul = (ll)mul * ksm((ll)sf[j] * isf[i - 1] % mod, (ll)(tA / i) * (tB / i) % (mod - 1)) % mod;
}
t1 = (ll)t1 * ksm(mul, (ll)(C / l) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod;
}
for (int l = 1, r; l <= n; l = r + 1)
{
r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
m = std::min(A / l, C / l); mul = 1; tA = A / l; tC = C / l;
for (int i = 1, j; i <= m; i = j + 1)
{
j = std::min(tA / (tA / i), tC / (tC / i));
mul = (ll)mul * ksm((ll)sf[j] * isf[i - 1] % mod, (ll)(tA / i) * (tC / i) % (mod - 1)) % mod;
}
t2 = (ll)t2 * ksm(mul, (ll)(B / l) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod;
}
ans2 = (ll)t1 * t2 % mod;
return (ll)ans1 * ksm(ans2, mod - 2) % mod;
}
int main()
{
int T; scanf("%d%d", &T, &mod); init(N - 1);
while (T--)
{
int A, B, C; scanf("%d%d%d", &A, &B, &C);
printf("%d %d %d\n", type0(A, B, C), type1(A, B, C), type2(A, B, C));
}
return 0;
}