1,[POI2007]ZAP-Queries
~~~题面~~~
题解: 首先列出式子:$$ans = \sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) == d]$$
$$[gcd(i, j) == d] = [gcd(\lfloor{\frac{i}{d}}\rfloor,\lfloor{\frac{j}{d}}\rfloor) == 1]$$
所以原式 $$\Rightarrow \quad \sum_{i = 1}^{\lfloor{\frac{n}{d}}\rfloor}\sum_{j = 1}^{\lfloor{\frac{m}{d}}\rfloor}[gcd(i, j)==1]$$
$$\Rightarrow \quad \sum_{i = 1}^{n}\sum_{j = 1}^{m}\sum_{d|gcd(i, j)}\mu(d)$$
因为$\mu(d)$会被统计到当且仅当$d | i \quad and \quad d | j$,即$d | gcd(i, j)$
那么考虑将满足条件的i和j两两搭配,组成的方案数就是$\mu(d)$会被统计到的次数,
也就是$\mu(d)$会被统计到$\lfloor{\frac{n}{d}}\rfloor\lfloor{\frac{m}{d}}\rfloor$次
$$\Rightarrow \quad ans=\sum_{i=1}^{min(n,m)}{\mu(d)\lfloor{\frac{n}{d}}\rfloor\lfloor{\frac{m}{d}}\rfloor}$$
然后观察到$\lfloor{\frac{n}{d}}\rfloor\lfloor{\frac{m}{d}}\rfloor$中有很多小段$\lfloor{\frac{n}{d}}\rfloor\lfloor{\frac{m}{d}}\rfloor$乘积是固定的,也就是$\lfloor{\frac{n}{d}}\rfloor$和$\lfloor{\frac{m}{d}}\rfloor$同时为一个固定的值,因此我们可以用整数分块优化
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 55000 5 int t, n, m, d; 6 int prime[AC], mu[AC], sum[AC], tot; 7 bool z[AC]; 8 9 inline int read() 10 { 11 int x = 0;char c = getchar(); 12 while(c > '9' || c < '0') c = getchar(); 13 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 14 return x; 15 } 16 17 void getprime() 18 { 19 int now; 20 mu[1] = 1; 21 for(R i = 2; i <= 50000; i++) 22 { 23 if(!z[i]) prime[++tot] = i, mu[i] = -1; 24 for(R j = 1; j <= tot; j++) 25 { 26 now = prime[j]; 27 if(now * i > 50000) break; 28 // printf("!!!%d %d\n", now, i); 29 z[now * i] = true; 30 if(!(i % now)) break; 31 mu[i * now] = -mu[i]; 32 } 33 } 34 for(R i = 1; i <= 50000; i++) 35 sum[i] = mu[i] + sum[i - 1]; 36 } 37 38 int ans; 39 40 void work() 41 { 42 int pos; 43 t = read(); 44 for(R i = 1; i <= t; i++) 45 { 46 n = read(), m = read(), d = read(); 47 n /= d, m /= d; 48 int b = min(n, m); 49 ans = 0; 50 for(R j = 1; j <= b; j = pos + 1) 51 { 52 pos = min(n / (n / j), m / (m / j)); 53 ans += (sum[pos] - sum[j-1]) * (n / j) * (m / j); 54 } 55 printf("%d\n", ans); 56 } 57 } 58 59 int main() 60 { 61 // freopen("in.in", "r", stdin); 62 getprime(); 63 work(); 64 // fclose(stdin); 65 return 0; 66 }