[HAOI2011]Problem b - 莫比乌斯反演
Description
对于给出的 \(n\) 个询问,每次求有多少个数对 \((x,y)\),满足 \(a \le x \le b\),\(c \le y \le d\),且 \(\gcd(x,y) = k\),\(\gcd(x,y)\) 函数为 \(x\) 和 \(y\) 的最大公约数。
其中 \(1 \le n,k \le 5 \times 10^4\),\(1 \le a \le b \le 5 \times 10^4\),\(1 \le c \le d \le 5 \times 10^4\)。
Solution
首先肯定是自然而然地想到分成四块容斥,即
\[\begin{split}
\sum \limits_{i=a}^{b} \sum \limits_{j=c}^{d} \left[ gcd(i,j) = k \right] &= \sum \limits_{i=1}^{b} \sum \limits_{j=1}^{d} \left[ gcd(i,j) =k \right] \\
&- \sum \limits_{i=1}^{a} \sum \limits_{j=1}^{d} \left[ gcd(i,j) =k \right]\\
&- \sum \limits_{i=1}^{b} \sum \limits_{j=1}^{c} \left[ gcd(i,j) =k \right]\\
&+ \sum \limits_{i=1}^{a} \sum \limits_{j=1}^{c} \left[ gcd(i,j) =k \right]
\end{split}
\]
这样,每一部分都是 \(\sum \limits_{i=1}^{m} \sum \limits_{j=1}^{m} \left[ gcd(i,j) = k \right]\) 的形式。
接下来考虑如何将式子进行变换:
\[\sum \limits_{i=1}^{m} \sum \limits_{j=1}^{m} \left[ gcd(i,j) = k \right] = \sum \limits_{i=1}^{\lfloor \frac{n}{k} \rfloor} \sum \limits_{j=1}^{\lfloor \frac{m}{k} \rfloor} \left[ gcd(i,j) = 1 \right]
\]
看到右边的 \(\left[ gcd(i,j) = 1 \right]\) 是不是感觉很熟悉?根据在莫比乌斯反演学习笔记中提到的反演结论
:
\[\left[ gcd(i,j) = 1 \right] \iff \sum \limits_{d \mid gcd(i,j)} \mu(d)
\]
可以继续化简:
\[\sum \limits_{i=1}^{\lfloor \frac{n}{k} \rfloor} \sum \limits_{j=1}^{\lfloor \frac{m}{k} \rfloor} \sum \limits_{d \mid gcd(i,j)} \mu(d)
\]
考虑变换求和顺序:
\[\sum \limits_{d=1}^{\min(n,m)} \mu(d) \sum \limits_{i=1}^{\lfloor \frac{n}{k} \rfloor} \left[ d \mid i \right] \sum \limits_{j=1}^{\lfloor \frac{m}{k} \rfloor} \left[ d \mid j \right]
\]
易知在 \(\left[ 1, n\right]\) 中约数包含 \(d\) 的数有 \(\lfloor \frac{n}{d} \rfloor\) 个,于是式子可以化简为:
\[\sum \limits_{d=1}^{\min(n,m)} \mu(d) \lfloor \frac{\lfloor \frac{n}{k} \rfloor}{d} \rfloor \lfloor \frac{\lfloor \frac{m}{k} \rfloor}{d} \rfloor
\]
再由莫比乌斯反演学习笔记中提到的引理1
,可得:
\[\sum \limits_{d=1}^{\min(n,m)} \mu(d) \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor
\]
所以最终,就得到了
\[\sum \limits_{i=a}^{b} \sum \limits_{j=c}^{d} \left[ gcd(i,j) = k \right] = \sum \limits_{d=1}^{\min(n,m)} \mu(d) \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor
\]
这个式子就可以利用数论分块进行求解了。
Code
Talk is cheap. Show me the code.
#include <bits/stdc++.h>
using namespace std;
int ty() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int _ = 5e4 + 10;
int tot, vis[_], p[_], mu[_], sum[_];
void getMu(int lim = 5e4) {
mu[1] = 1;
for (int i = 2; i <= lim; ++i) {
if (!vis[i]) p[++tot] = i, mu[i] = -1;
for (int j = 1; j <= tot && p[j] * i <= lim; ++j) {
vis[i * p[j]] = 1;
if (i % p[j] == 0) {
mu[i * p[j]] = 0;
break;
}
mu[i * p[j]] = -mu[i];
}
}
for (int i = 1; i <= lim; ++i) sum[i] = sum[i - 1] + mu[i];
}
int calc(int n, int m, int k) {
n /= k, m /= k;
int ret = 0;
for (int l = 1, r; l <= min(n, m); l = r + 1) {
r = min(n / (n / l), m / (m / l));
ret += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
}
return ret;
}
int main() {
getMu();
int T = ty(), a, b, c, d, k;
while (T--) {
a = ty(), b = ty(), c = ty(), d = ty(), k = ty();
printf("%d\n", calc(b, d, k) - calc(a - 1, d, k) - calc(b, c - 1, k) +
calc(a - 1, c - 1, k));
}
return 0;
}
既然选择了远方,便只顾风雨兼程。