题面
解法
其实很久之前就想写这道题了……
- 首先我们先考虑一下 怎么处理。
- 有一个结论: ,具体证明可以看popoqqq大爷的证明:链接。
- 然后我们就可以把式子写成这样:
- 感觉后面的整除并不是那么方便,不妨把 提前到前面枚举,就变成:
- 然后就变成比较熟悉的式子了,反演一下可以得到
- 有一个比较简单的结论: ,那么我们就可以令 。然后式子就变成
- 令 ,然后我们可以发现 。这个式子其实也挺好证的,只要看每一个因子的贡献就可以了。
- 那么式子就变成了 。
- 那么我们发现就可以数论分块了,我们只需要预处理出 数组和 的前缀和就可以了
- 时间复杂度:
代码
#include <bits/stdc++.h>
#define ll long long
#define N 50010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
bool f[N]; int p[N];
ll d[N], mu[N];
void sieve(int n) {
memset(f, true, sizeof(f)); int len = 0; mu[1] = 1;
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j += i)
d[j]++;
for (int i = 2; i <= n; i++) {
if (f[i]) p[++len] = i, mu[i] = -1;
for (int j = 1; j <= len && i * p[j] <= n; j++) {
int k = i * p[j]; f[k] = false;
if (i % p[j] == 0) {mu[k] = 0; break;}
mu[k] = -mu[i];
}
}
for (int i = 1; i <= n; i++) d[i] += d[i - 1], mu[i] += mu[i - 1];
}
ll solve(int n, int m) {
ll ret = 0, x = 0;
for (int i = 1; i <= n; i = x + 1) {
x = min(n / (n / i), m / (m / i));
ret += 1ll * d[n / i] * d[m / i] * (mu[x] - mu[i - 1]);
}
return ret;
}
int main() {
sieve(5e4); int T; read(T);
while (T--) {
int n, m; read(n), read(m);
if (n > m) swap(n, m);
cout << solve(n, m) << "\n";
}
return 0;
}