# luogu P3455 [POI2007]ZAP-Queries （莫比乌斯反演 + 整除分块）

## 题目传送门

• f ( n ) f(n) 表示规定范围内 g c d ( x , y ) = n gcd(x,y)=n 的数对个数
• F ( n ) F(n) 表示规定范围内公约数包括 n n 的数对个数（即 n ∣ g c d n|gcd 的数对个数），也可以写成 F ( t ) = F(t)= 满足 g c d ( x , y ) % n = = 0 gcd(x,y)\%n==0 的数对个数

f ( k ) = ∑ i = 1 a ∑ j = 1 b [ g c d ( i , j ) = k ] f(k)=\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j)=k]

F ( n ) = ∑ n ∣ k f ( k ) F(n)=\sum_{n|k}f(k)

f ( d ) = ∑ d ∣ k μ ( ⌊ k d ⌋ ) F ( k ) f(d)=\sum_{d|k}\mu(\lfloor\frac{k}{d}\rfloor)F(k)

f ( d ) = ∑ d ∣ k μ ( ⌊ k d ⌋ ) ⌊ a k ⌋ ⌊ b k ⌋ f(d)=\sum_{d|k}\mu(\lfloor\frac{k}{d}\rfloor)\lfloor\frac{a}{k}\rfloor\lfloor\frac{b}{k}\rfloor

f ( d ) = ∑ t = 1 m i n ( a , b ) μ ( t ) ⌊ a t d ⌋ ⌊ b t d ⌋ f(d)=\sum_{t=1}^{min(a,b)}\mu(t)\lfloor\frac{a}{td}\rfloor\lfloor\frac{b}{td}\rfloor

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>

using namespace std;
typedef long long ll;
const int N = 50007, M = 50007, INF = 0x3f3f3f3f;
const double eps = 1e-6;

int n, m;
int vis[N];
int cnt, mu[N], prime[N];
int sum[N];

void get_mu(int n)
{
memset(vis, 0, sizeof vis);
memset(mu, 0, sizeof mu);
cnt = 0, mu[1] = 1;
for(int i = 2; i <= n; ++ i){
if(!vis[i]){
prime[cnt ++ ] = i;
mu[i] = -1;
}
for(int j = 0; j < cnt && prime[j] * i <= n; ++ j){
vis[prime[j] * i] = true;
if(i % prime[j] == 0)break;
mu[i * prime[j]] = - mu[i];
}
}
for(int i = 1; i <= n; ++ i){
sum[i] = sum[i - 1] + mu[i];
}
}

int main()
{
get_mu(5e4 + 7);
scanf("%d", &n);
while(n -- ){
int a, b, d;
ll ans = 0;
scanf("%d%d%d", &a, &b, &d);
// l 从一定要1开始！从0开始的话就会 除0 直接RE
//要保证l <= min(a, b)因为一旦t大于a，就会除0（l就是枚举的t，t=k/d）
for(int l = 1, r; l <= min(a, b); l = r + 1){
r = min(a / (a / l), b / (b / l));
ans += 1ll * (a / (l * d)) * (b / (l *d)) * (sum[r] - sum[l - 1]);
}
printf("%lld\n", ans);
}
}

02-20 786