主要思路和推导
这道题还是比较有意思的(啊呀看了好久的题解才会写,真菜),主要就是把\(d\)转换成了:
\[ d(xy) = \sum_{i|x}^x \sum_{j|y}^y [\gcd(i,j) = 1] \]
可以感性理解一下:每一次约数被枚举出来的时候,只有互质的情况下才会被计数,避免了重复计数。
所以我们可以试着化简一下:
\[ \sum^N_{i=1} \sum^M_{j=1} d(i,j) = \sum^N_{i=1} \sum^M_{j=1} \sum_{x|i}^i \sum_{y|j}^j [\gcd(x,y) = 1] \\ \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [\gcd(x,y) = 1] \]
可以进行莫比乌斯反演了:
\[f(h) = \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [\gcd(x,y) = h] \\ F(h) = \sum_{h|d} f(h) \\ F(h) = \sum_{h|d} \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [\gcd(x,y) = d] \\ F(h) = \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [h|\gcd(x,y)] \\ F(h) = \sum^{\frac{N}{h}}_{i=1} \sum^{\frac{M}{h}}_{j=1} \lfloor \frac{N}{hi} \rfloor \lfloor \frac{M}{hj} \rfloor \\ f(h)=\sum_{h|d} \mu(\frac{d}{h})F(h) \\ f(h)=\sum_{h|d} \mu(\frac{d}{h}) \sum^{\frac{N}{h}}_{i=1} \sum^{\frac{M}{h}}_{j=1} \lfloor \frac{N}{hi} \rfloor \lfloor \frac{M}{hj} \rfloor \\ f(h)=\sum_{h|d} \mu(\frac{d}{h}) (\sum^{\frac{N}{h}}_{i=1} \lfloor \frac{N}{hi} \rfloor) (\sum^{\frac{M}{h}}_{j=1} \lfloor \frac{M}{hj} \rfloor) \]
后面的分数部分我们直接设个函数预处理即可:
\[ \sum^{\frac{N}{h}}_{i=1} \lfloor \frac{N}{hi} \rfloor = \sum^{\frac{N}{h}}_{i=1} \lfloor \frac{N}{h} * \frac{1}{i} \rfloor \]
可见得我们把\( \frac{N}{h} \)作为\(x\),预处理出\(s(x) = \sum_{i=1}^x \frac{x}{i}\),然后整除分块搞下复杂度就可以降到很低了。
代码
// P3327.cpp #include <bits/stdc++.h> #define ll long long using namespace std; const int MAX_N = 50100; int n, prime[MAX_N], tot, mu[MAX_N], muprefix[MAX_N]; ll g[MAX_N]; bool vis[MAX_N]; int main() { scanf("%d", &n); mu[1] = 1; for (int i = 2; i < MAX_N; i++) { if (!vis[i]) prime[++tot] = i, mu[i] = -1; for (int j = 1; j <= tot && i * prime[j] < MAX_N; j++) { vis[i * prime[j]] = true; if (i % prime[j] == 0) break; else mu[i * prime[j]] = -mu[i]; } } for (int i = 1; i < MAX_N; i++) muprefix[i] = muprefix[i - 1] + mu[i]; for (int i = 1; i < MAX_N; i++) { ll ans = 0; for (int l = 1, r = 0; l <= i; l = r + 1) { r = (i / (i / l)); ans += 1LL * (r - l + 1) * 1LL * (i / l); } g[i] = ans; } while (n--) { int a, b; scanf("%d%d", &a, &b); ll ans = 0; for (int l = 1, r; l <= min(a, b); l = r + 1) { r = min(a / (a / l), b / (b / l)); ans += (muprefix[r] - muprefix[l - 1]) * 1LL * g[a / l] * 1LL * g[b / l]; } printf("%lld\n", ans); } return 0; }