题面
解法
其实很久之前就想写这道题了……
- 首先我们先考虑一下 d ( i j ) d(ij) d(ij)怎么处理。
- 有一个结论: d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = = 1 ] d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)==1] d(ij)=∑x∣i∑y∣j[gcd(x,y)==1],具体证明可以看popoqqq大爷的证明:链接。
- 然后我们就可以把式子写成这样: ∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = = 1 ] \sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[gcd(x,y)==1] ∑i=1n∑j=1m∑x∣i∑y∣j[gcd(x,y)==1]
- 感觉后面的整除并不是那么方便,不妨把 x , y x,y x,y提前到前面枚举,就变成: ∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ m y ⌋ [ g c d ( x , y ) = = 1 ] \sum_{x=1}^n\sum_{y=1}^m\lfloor\frac{n}{x}\rfloor\lfloor\frac{m}{y}\rfloor[gcd(x,y)==1] ∑x=1n∑y=1m⌊xn⌋⌊ym⌋[gcd(x,y)==1]
- 然后就变成比较熟悉的式子了,反演一下可以得到 ∑ d μ ( d ) ( ∑ i = 1 ⌊ n d ⌋ ⌊ n i d ⌋ ) ( ∑ j = 1 ⌊ m d ⌋ ⌊ m j d ⌋ ) \sum_d\mu(d)(\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{n}{id}\rfloor)(\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\lfloor\frac{m}{jd}\rfloor) ∑dμ(d)(∑i=1⌊dn⌋⌊idn⌋)(∑j=1⌊dm⌋⌊jdm⌋)
- 有一个比较简单的结论: ⌊ ⌊ n x ⌋ y ⌋ = ⌊ n x y ⌋ \lfloor\frac{\lfloor\frac{n}{x}\rfloor}{y}\rfloor=\lfloor\frac{n}{xy}\rfloor ⌊y⌊xn⌋⌋=⌊xyn⌋,那么我们就可以令 n ′ = ⌊ n d ⌋ , m ′ = ⌊ m d ⌋ n'=\lfloor\frac{n}{d}\rfloor,m'=\lfloor\frac{m}{d}\rfloor n′=⌊dn⌋,m′=⌊dm⌋。然后式子就变成 ∑ d μ ( d ) ( ∑ i = 1 n ′ ⌊ n ′ i ⌋ ) ( ∑ j = 1 m ′ ⌊ m ′ j ⌋ ) \sum_d\mu(d)(\sum_{i=1}^{n'}\lfloor\frac{n'}{i}\rfloor)(\sum_{j=1}^{m'}\lfloor\frac{m'}{j}\rfloor) ∑dμ(d)(∑i=1n′⌊in′⌋)(∑j=1m′⌊jm′⌋)
- 令 s [ n ] = ∑ i = 1 n d ( i ) s[n]=\sum_{i=1}^nd(i) s[n]=∑i=1nd(i),然后我们可以发现 ∑ i = 1 n ⌊ n i ⌋ = s [ n ] \sum_{i=1}^n\lfloor\frac{n}{i}\rfloor=s[n] ∑i=1n⌊in⌋=s[n]。这个式子其实也挺好证的,只要看每一个因子的贡献就可以了。
- 那么式子就变成了 ∑ d μ ( d ) s [ ⌊ n d ⌋ ] s [ ⌊ m d ⌋ ] \sum_d\mu(d)s[\lfloor\frac{n}{d}\rfloor]s[\lfloor\frac{m}{d}\rfloor] ∑dμ(d)s[⌊dn⌋]s[⌊dm⌋]。
- 那么我们发现就可以数论分块了,我们只需要预处理出 s s s数组和 μ \mu μ的前缀和就可以了
- 时间复杂度: O ( T n ) O(T\sqrt n) O(Tn)
代码
#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;
}