【题解】Luogu-P7486 「Stoi2031」彩虹
请确保宁已经熟练掌握:
- 莫比乌斯反演
- 形如 \(\sum_{d\mid n} f(d)\),其中 \(f\) 为积性函数的 \(\Theta(n\ln n)\) 的筛法。
Description
-
多测,数据组数为 \(t\)。
-
给定一个正整数 \(n\),然后 \(t\) 组询问,每组给出两个满足 \(1\le l\le r\le n\) 的正整数 \(l, r\),请求出
\[\left[\prod_{i = l}^r \prod_{j = l}^r \operatorname{lcm}(i, j)^{\operatorname{lcm}(i, j)} \right] \bmod 32465177 \] -
对于 \(100\%\) 的数据,\(1 \le n \le 10^6, 1 \le t \le 10, 1 \le l_i \le r_i \le n\)。
Solution
考虑用前缀积 + 容斥。
为方便表示,设 \(f(n, m) = \prod_{i = 1}^n \prod_{i = 1}^m \operatorname{lcm}(i, j)^{\operatorname{lcm}(i, j)}\)。
则
观察到 \(f(n, m) = f(m, n)\),不妨设 \(n\le m\)。
发现若 \(\gcd(i, j)\ne d\),则 \(\dfrac{ij}{\gcd(i, j)} [\gcd(i, j) = d] = 0\),那么如果它作为指数,有 \(\left[\dfrac{ij}{\gcd(i, j)} \right]^{\frac{ij}{\gcd(i, j)} [\gcd(i, j) = d]} = 1\),刚好满足这一项不符合要求。
所以变成
将指数的加改为底数的乘
化简后面两项。设一个函数 \(S(n) = \sum_{i = 1}^n i\),再设 \(g(n) = \prod_{i = 1}^n i^i\)。
则
设它为 \(h(n, m)\) 吧。
然后
回代
设 \(\alpha(n) = \sum_{d\mid n} d\, \mu(d)\),另设 \(\beta(n) = \prod_{d\mid n} d^{d\, \mu(d)}\)。
则
为简洁表示,再设 \(\delta(n) = \left[\beta(n) \cdot n^{\alpha(n)} \right]^n\)。
数论分块:
- 第一项底数相同,预处理指数的前缀和即可;
- 第二项指数相同,预处理底数的前缀积即可。
模数 \(p = 32465177\) 是质数,根据欧拉定理可以将指数 \(\bmod (p - 1)\) 降到 \(\Omicron(p)\) 级别。
以上所有需要用到的东西:
- \(S(n) = \sum_{i = 1}^n i\):\(\Theta(1)\) 计算,\(S(n)\) 都作为指数出现,所以 \(\bmod (p - 1)\)。
- \(g(n) = \prod_{i = 1}^n i^i\):预处理 \(\Omicron(n\log n)\),查询 \(\Theta(1)\)。
- \(h(n, m) = g(n)^{S(m)} \cdot g(m)^{S(n)}\):在可以 \(\Theta(1)\) 得到 \(S,g\) 的值的情况下是 \(\Omicron(\log p)\) 计算的。
- \(d\, \mu(d)\):令其为 \(\gamma(d)\)。把这个在线性筛时顺便算出来,\(\Theta(n)\)。观察到 \(\gamma(d)\) 都作为指数出现,所以可以将其 \(\bmod (p - 1)\) 并转成正数。
- \(\alpha(n) = \sum_{d\mid n} \gamma(d)\):预处理 \(\Theta(n\ln n)\),查询 \(\Theta(1)\)。作为指数出现,\(\bmod (p - 1)\)。
- \(\alpha(n) n\):令其为 \(\zeta(n)\)。预处理 \(\Theta(n)\),查询 \(\Theta(1)\)。作为指数出现,\(\bmod (p - 1)\)。
- \(d^{d\, \mu(d)}\):令其为 \(\lambda(d)\)。也是在线性筛中做,但它使线性筛多了个 \(\log p\)。
- \(\beta(n) = \prod_{d\mid n} \lambda(d)\):预处理 \(\Theta(n\ln n)\),查询 \(\Theta(1)\)。
- \(\delta(n) = \left[\beta(n) \cdot n^{\alpha(n)} \right]^n\):预处理 \(\Omicron(n\log p)\),查询 \(\Theta(1)\)。
数论分块是 \(\Omicron\left(\sqrt{n}\log p\right)\) 的,即计算 \(f(n, m) = \prod_{i = 1}^n \prod_{i = 1}^m \operatorname{lcm}(i, j)^{\operatorname{lcm}(i, j)}\) 是 \(\Omicron\left(\sqrt{n}\log p\right)\) 的。
\(t\) 次询问,时间是 \(\Omicron(t\sqrt{n}\log p)\) 的。
加上预处理,总时间复杂度为 \(\Omicron(n\log p + n\ln n + t\sqrt{n}\log p)\) 的,由于 \(t\le 10\) 过小,所以真正在 \(\Omicron(n\log p)\) 级别内。
Code
// 18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;
const int MAXN = 1e6 + 5;
const int MOD = 32465177;
int qpow(int a, int b)
{
int base = a, ans = 1;
while (b)
{
if (b & 1)
{
ans = (ll)ans * base % MOD;
}
base = (ll)base * base % MOD;
b >>= 1;
}
return ans;
}
int inv(int a)
{
return qpow(a, MOD - 2);
}
int p[MAXN], g[MAXN], mu[MAXN], gamma[MAXN], alpha[MAXN], zeta[MAXN], zeta_sum[MAXN], lambda[MAXN], beta[MAXN], delta[MAXN], delta_pro[MAXN], delta_pro_inv[MAXN], delta_pro_pro[MAXN], delta_pro_pro_inv[MAXN];
bool vis[MAXN];
void pre(int n)
{
g[1] = mu[1] = gamma[1] = lambda[1] = beta[1] = delta[1] = 1;
for (int i = 2; i <= n; i++)
{
beta[i] = 1;
g[i] = (ll)g[i - 1] * qpow(i, i % (MOD - 1)) % MOD;
if (!vis[i])
{
p[++p[0]] = i;
mu[i] = -1;
}
gamma[i] = (i * mu[i] % (MOD - 1) + MOD - 1) % (MOD - 1);
lambda[i] = qpow(i, gamma[i]);
for (int j = 1; j <= p[0] && i * p[j] <= n; j++)
{
vis[i * p[j]] = true;
if (i % p[j] == 0)
{
mu[i * p[j]] = 0;
break;
}
mu[i * p[j]] = mu[i] * mu[p[j]];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; i * j <= n; j++)
{
alpha[i * j] = (alpha[i * j] + gamma[i]) % (MOD - 1);
beta[i * j] = (ll)beta[i * j] * lambda[i] % MOD;
}
}
delta_pro[0] = delta_pro_pro[0] = delta_pro_inv[0] = delta_pro_inv[1] = 1;
for (int i = 1; i <= n; i++)
{
zeta[i] = (ll)alpha[i] * i % (MOD - 1);
zeta_sum[i] = (zeta_sum[i - 1] + zeta[i]) % (MOD - 1);
delta[i] = qpow((ll)beta[i] * qpow(i, alpha[i]) % MOD, i % (MOD - 1));
delta_pro[i] = (ll)delta_pro[i - 1] * delta[i] % MOD;
delta_pro_pro[i] = (ll)delta_pro_pro[i - 1] * delta_pro[i] % MOD;
}
delta_pro_pro_inv[n] = inv(delta_pro_pro[n]);
for (int i = n - 1; i >= 1; i--)
{
delta_pro_pro_inv[i] = (ll)delta_pro_pro_inv[i + 1] * delta_pro[i + 1] % MOD;
delta_pro_inv[i + 1] = (ll)delta_pro_pro_inv[i + 1] * delta_pro_pro[i] % MOD;
}
}
int S(int n)
{
return (ll)n * (n + 1) / 2 % (MOD - 1);
}
int h(int n, int m)
{
return (ll)qpow(g[n], S(m)) * qpow(g[m], S(n)) % MOD;
}
int GetZetaSum(int l, int r)
{
return (zeta_sum[r] - zeta_sum[l - 1] + MOD - 1) % (MOD - 1);
}
int GetDeltaPro(int l, int r)
{
return (ll)delta_pro[r] * delta_pro_inv[l - 1] % MOD;
}
int f(int n, int m)
{
if (n > m)
{
swap(n, m);
}
int res = 1;
for (int l = 1, r; l <= n; l = r + 1)
{
int k1 = n / l, k2 = m / l;
r = min(n / k1, m / k2);
res = (ll)res * qpow(h(k1, k2), GetZetaSum(l, r)) % MOD * qpow(GetDeltaPro(l, r), (ll)S(k1) * S(k2) % (MOD - 1)) % MOD;
}
return res;
}
int main()
{
int t, n;
scanf("%d%d", &t, &n);
pre(n);
while (t--)
{
int l, r;
scanf("%d%d", &l, &r);
int a = (ll)f(r, r) * f(l - 1, l - 1) % MOD, b = (ll)f(r, l - 1) * f(l - 1, r) % MOD;
printf("%d\n", (ll)a * inv(b) % MOD);
}
return 0;
}