询问拆成四个,就像矩阵数点一样。
每一个询问的形式为 $\sum_{i=1}^n\sum_{j=1}^m[(i,j)==k]$。
$$\sum_{i=1}^n\sum_{j=1}^m[(i,j)==k]=\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}[(i,j)==1]$$
把 $\lfloor \dfrac{n}{k} \rfloor$ 换成 $n$,把 $\lfloor \dfrac{m}{k} \rfloor$ 换成 $m$
$$\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|(i,j)}\mu(d)=\sum_d \mu(d)\sum_{i=1}^{n}[d|i]\sum_{j=1}^{m}[d|j]$$
$$=\sum_d\mu(d)\lfloor \dfrac{n}{d}\rfloor\lfloor \dfrac{m}{d}\rfloor$$
求个 $\mu$ 的前缀和加数论分块。
#include <bits/stdc++.h> const int N = 5e4 + 7; int prime[N], prin, mu[N]; bool vis[N]; void init() { mu[1] = 1; for (int i = 2; i < N; i++) { if (!vis[i]) prime[++prin] = i, mu[i] = -1; for (int j = 1; j <= prin && i * prime[j] < N; j++) { vis[i * prime[j]] = 1; if (i % prime[j] == 0) { mu[i * prime[j]] = 0; break; } mu[i * prime[j]] = -mu[i]; } } for (int i = 1; i < N; i++) mu[i] += mu[i - 1]; } int solve(int n, int m) { int res = 0; for (int i = 1, j; i <= std::min(n, m); i = j + 1) { j = std::min(n / (n / i), m / (m / i)); res += (mu[j] - mu[i - 1]) * (n / i) * (m / i); } return res; } int main() { init(); int n; scanf("%d", &n); while (n--) { int a, b, c, d, k; scanf("%d%d%d%d%d", &a, &b, &c, &d, &k); printf("%d\n", solve(b / k, d / k) - solve(b / k, (c - 1) / k) - solve((a - 1) / k, d / k) + solve((a - 1) / k, (c - 1) / k)); } return 0; }View Code