「题解」[LG5221] Product
化简指数:
带回原式:
这个式子显然可以整除分块求解,问题转换为求 \(g(n)=\displaystyle\prod_{d\mid n}d^{\mu\left(\frac{n}{d}\right)}\) 的前缀积。
如果空间允许的话,我们可以直接预处理出 \(\mu,d,d^{-1}\),然后暴力枚举倍数求 \(g\),时间复杂度 \(\Theta(n \ln n)\)。但是 7.81MB 的空间限制不允许我们这么做。
分析一下 \(g\) 是否有特殊性质。
考虑 \(n\) 的唯一分解形式 \(n=\displaystyle\prod_{i=1}^{m}p_i^{k_i}\),则:
-
当 \(m=1\) 时,有 \(\displaystyle g(n)=n\times\left(p_1^{k_1-1}\right)^{-1}=p_1\)。
-
当 \(m\neq 1\) 时,有 \(g(n)=1\),证明如下:
由于 \(\mu\) 的定义,我们只需要考虑 \(\dfrac{n}{d}\) 不包含平方因子的情况。也就是说,\(\dfrac{n}{d}\) 一定是 \(\{p_1,p_2,p_3,\dots,p_m\}\) 的一个子集(记为 \(S\))内所有数的乘积(记为 \(c\)),当这个 \(|S|\) 为奇数时,\(g(n)\) 乘上 \(\left(\dfrac{n}{c}\right)^{-1}\),反之 \(g(n)\) 乘上 \(\dfrac{n}{c}\)。
我们对每一个 \(p_i\) 计算其对 \(g(n)\) 的贡献,有四种情况:
-
\(p_i \in S\) ,且 \(|S|\) 为奇数,此时 \(p_i\) 对 \(g(n)\) 的贡献为 \(\displaystyle p_i^{ -\left(k_i-1\right)2^{m-2} }\);
(在剩下的 \(m - 1\) 个数中选奇/偶数个数均有 \(2^{m-2}\) 种方案,因此指数乘上 \(2^{m-2}\),下同) -
\(p_i \in S\) ,且 \(|S|\) 为偶数,此时 \(p_i\) 对 \(g(n)\) 的贡献为 \(\displaystyle p_i^{ \left(k_i-1\right)2^{m-2} }\);
-
\(p_i \not\in S\) ,且 \(|S|\) 为奇数,此时 \(p_i\) 对 \(g(n)\) 的贡献为 \(\displaystyle p_i^{ -k_i2^{m-2} }\);
-
\(p_i \not\in S\) ,且 \(|S|\) 为偶数,此时 \(p_i\) 对 \(g(n)\) 的贡献为 \(\displaystyle p_i^{ k_i2^{m-2} }\)。
这四种情况的贡献相乘 \(=1\),也就是说对于每一个 \(p_i\) 其对 \(g(n)\) 的贡献均为 \(1\)。因此 \(g(n)=1\)。
-
即:
使用线性筛筛出所有的质数,然后对于每个质数 \(p\),暴力枚举 \(p^k\) 即可求出 \(g\)。
综上,我们得到了一个时间复杂度 \(\Theta(n)\) 并且使用空间 < 7.81MiB 的做法。
由于实际运用中埃氏筛的运行效率高于线性筛,因此代码中使用的是埃氏筛筛质数。
代码
#include <bits/stdc++.h>
#define reg register
using namespace std;
const uint N = 1e6, P = 104857601;
uint n, fac = 1, ans = 1, ptot = 1, pnum[78450], g[N + 1];
bitset<N + 1> isnp;
inline uint qpow(reg uint a, reg uint b) {
reg uint ret = 1;
for (; b; b >>= 1, a = 1ull * a * a % P) b & 1 ? ret = 1ull * ret * a % P : 0;
return ret;
}
signed main() {
scanf("%u", &n), pnum[g[0] = g[1] = 1] = 2;
for (reg uint i = 3; i * i <= n; i += 2) if (isnp[i] == 0) for (reg uint j = i * i; j <= n; j += 2 * i) isnp.set(j);
for (reg uint i = 3; i <= n; i += 2) if (isnp[i] == 0) pnum[++ptot] = i;
for (reg uint i = 1; i <= ptot; i++) {
g[pnum[i]] = pnum[i];
for (reg uint j = pnum[i], k; 1ull * j * pnum[i] <= n;) g[j *= pnum[i]] = pnum[i];
}
for (reg uint i = 2; i <= n; i++) fac = 1ull * fac * i % P, g[i] = 1ull * (g[i] ? g[i] : 1) * g[i - 1] % P;
for (reg uint l = 1, r, k; l <= n; l = r + 1)
k = n / l, r = n / k, ans = 1ull * ans * qpow(1ull * g[r] * qpow(g[l - 1], P - 2) % P, 1ull * k * k % (P - 1)) % P;
return printf("%u\n", 1ull * qpow(fac, 2 * n) * qpow(1ull * ans * ans % P, P - 2) % P), 0;
}