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 }
View Code

相关文章: