题解:P13645 Totient with Divisors

· · 题解

利用恒等式:

:::info[理性证明]{open} 设 $i=\prod_p p^{\alpha_p},j=\prod_p p^{\beta_p}$。 右侧对不同质因数可以分别计算。因为 $\mu(p^e)=0$ 对所有 $e\ge2$ 成立,所以对质数 $p$,局部贡献只可能包含 $d=1$ 和 $d=p$。 不妨设 $\alpha\le\beta$。 当 $\alpha=0$ 时,$p\nmid\gcd(i,j)$,局部贡献只有 $\sigma(p^\beta)$。 当 $\alpha\ge1$ 时,局部贡献为 $\sigma(p^\alpha)\sigma(p^\beta) -p\sigma(p^{\alpha-1})\sigma(p^{\beta-1})$。 利用等比数列求和公式 $\sigma(p^x)=\frac{1-p^{x+1}}{1-p}$。 代入可得:

\begin{aligned} &\sigma(p^\alpha)\sigma(p^\beta) -p\sigma(p^{\alpha-1})\sigma(p^{\beta-1})\ ={}& \frac{(1-p^{\alpha+1})(1-p^{\beta+1}) -p(1-p^\alpha)(1-p^\beta)} {(1-p)^2}\ ={}& \frac{1-p+p^{\alpha+\beta+2}-p^{\alpha+\beta+1}} {(1-p)^2}\ ={}& \frac{(1-p)(1-p^{\alpha+\beta+1})} {(1-p)^2}\ ={}& \frac{1-p^{\alpha+\beta+1}}{1-p}\ ={}& \sigma(p^{\alpha+\beta}) \end{aligned}

这正是 $p$ 在 $\sigma(ij)$ 中的局部贡献。 所有质数的贡献相乘后得到 $\prod_p\sigma\left(p^{\alpha_p+\beta_p}\right) =\sigma(ij)$。 因此恒等式成立。 ::: 代入原式并交换求和:$\operatorname{Ans}(n,m)=\sum_{d\le\min(n,m)}\mu(d)dF_d(n)F_d(m)$, 其中:$F_d(x)=\sum_{u=1}^{\left\lfloor x/d\right\rfloor}\varphi(du)\sigma(u)$。 设所有询问均满足 $n\le m$,最大值分别为 $X,Y$。取阈值 $B\approx\sqrt{\frac{XY}{T}}$。 对于 $d\le B$,预处理 $F_d(x)$ 的前缀和,每次询问直接累加。 对于 $d>B$,按较小端点 $n$ 离线扫描。扫描到 $x$ 时,枚举满足 $d\mid x$ 的大因子,并把 $\mu(d)d\varphi(x)\sigma\left(\frac{x}{d}\right)\varphi(dv)\sigma(v)$ 加入位置 $dv$。这样查询纵坐标前缀 $m$,即可得到所有 $i\le n,j\le m$ 的贡献。单点增加与前缀查询使用根号分块维护。 时间复杂度为 $\mathcal O\left(Y\log X+TB+\frac{XY}{B}+T\sqrt Y\right)$。 ```cpp #include <bits/stdc++.h> using namespace std; #define ll long long const int N = 1e5 + 5, Q = 1e5 + 5, P = 998244353, H = 9e5 + 5; int T, qn[Q], qm[Q], ans[Q], nx[Q], head[N]; int mu[N], phi[N], sg[N], pr[N], pc, vis[N], cf[N]; int sd[N], sc, off[N], pre[H], pt; int cnt[N], bg[N], cur[N], dv[H]; int wo[N], ww[H], wt; int val[N], bs[N], S; inline void add(int x, int v) { int y = val[x] + v; if (y >= P) y -= P; val[x] = y; int b = (x - 1) / S; y = bs[b] + v; if (y >= P) y -= P; bs[b] = y; } inline int ask(int x) { int b = (x - 1) / S; ll s = 0; for (int i = 0; i < b; i++) { s += bs[i]; } for (int i = b * S + 1; i <= x; i++) { s += val[i]; } return s % P; } int main() { cin >> T; int X = 0, Y = 0; for (int i = 0; i < T; i++) { cin >> qn[i] >> qm[i]; if (qn[i] > qm[i]) swap(qn[i], qm[i]); X = max(X, qn[i]); Y = max(Y, qm[i]); } mu[1] = phi[1] = 1; for (int i = 2; i <= Y; i++) { if (!vis[i]) { pr[pc++] = i; mu[i] = -1; phi[i] = i - 1; } for (int j = 0; j < pc && i * pr[j] <= Y; j++) { int p = pr[j], x = i * p; vis[x] = 1; if (i % p == 0) { mu[x] = 0; phi[x] = phi[i] * p; break; } mu[x] = -mu[i]; phi[x] = phi[i] * (p - 1); } } for (int d = 1; d <= Y; d++) { for (int j = d; j <= Y; j += d) { int x = sg[j] + d; if (x >= P) x -= P; sg[j] = x; } } for (int i = 1; i <= Y; i++) { if (mu[i]) { cf[i] = mu[i] > 0 ? i : P - i; } } int B = sqrt((long double)X * Y / T); if (B < 1) B = 1; if (B > X) B = X; for (int d = 1; d <= B; d++) { if (mu[d]) { sd[sc++] = d; off[d] = pt; pre[pt++] = 0; for (int u = 1; u <= Y / d; u++) { pre[pt++] = (pre[pt - 1] + (ll)phi[d * u] * sg[u]) % P; } } } for (int i = 0; i < T; i++) { ll s = 0; for (int j = 0; j < sc && sd[j] <= qn[i]; j++) { int d = sd[j]; int x = pre[off[d] + qn[i] / d]; int y = pre[off[d] + qm[i] / d]; s += (ll)cf[d] * x % P * y % P; if (s >= (1LL << 62)) s %= P; } ans[i] = s % P; } for (int d = B + 1; d <= X; d++) { if (mu[d]) { wo[d] = wt; ww[wt++] = 0; for (int u = 1; u <= Y / d; u++) { ww[wt++] = (ll)phi[d * u] * sg[u] % P; } for (int x = d; x <= X; x += d) { cnt[x]++; } } } bg[1] = 0; for (int x = 1; x <= X; x++) { bg[x + 1] = bg[x] + cnt[x]; cur[x] = bg[x]; } for (int d = B + 1; d <= X; d++) { if (mu[d]) { for (int x = d; x <= X; x += d) { dv[cur[x]++] = d; } } } memset(head, -1, sizeof(int) * (X + 1)); for (int i = 0; i < T; i++) { nx[i] = head[qn[i]]; head[qn[i]] = i; } S = sqrt(2.0 * Y) + 1; for (int x = 1; x <= X; x++) { for (int j = bg[x]; j < bg[x + 1]; j++) { int d = dv[j], u = x / d, o = wo[d]; int base = (ll)cf[d] * ww[o + u] % P; for (int v = 1, y = d; v <= Y / d; v++, y += d) { add(y, (ll)base * ww[o + v] % P); } } for (int i = head[x]; i != -1; i = nx[i]) { ans[i] += ask(qm[i]); if (ans[i] >= P) ans[i] -= P; } } for (int i = 0; i < T; i++) { cout << ans[i] << endl; } return 0; } ```