[HAOI2011]Problem b

莫比乌斯反演 + 整除分块。

关于整除分块:

对于 i <= x <= n / (n / i) 的 x

有 n / x = n / i

证明略,记住就好(滑稽)

1 for(int i = 1, j; i <= n; i = j + 1) {
2     j = n / (n / i);
3     // j = std::(m / (m / i), n / (n / i)); 
4     // 操作区间[i, j] 
5 }
代码实现

接下来看题:要求[a,b] ~ [c,d]中 gcd == k 的个数。

按照套路,设

f(x) = sum[x == g],F(x) = sum[x | g]

发现离解题还差的很远....

下一步:发现可以用类似二维前缀和的方式来求解。

问题转化为求 n, m 中 gcd == k

然后就很熟悉了...

然后就T了.......

注意到F(x) = (n / x) * (m / x),可以整除分块。

有个结论就是:

然后就可以做了。

我本人不能想到这个结论,于是提供另一种思路:

gcd(i, j) == k => gcd(i / k, j / k) == 1

在这两者之间可以建立一一对应的映射。

于是我们把m, n都/k,就成了求f(1),这个时候就是裸的整除分块了。

 1 #include <cstdio>
 2 #include <algorithm>
 3 const int N = 50010;
 4 
 5 int sum[N], miu[N], p[N], top, k;
 6 bool vis[N];
 7 
 8 inline int F(int x, int n, int m) {
 9     return (n / x) * (m / x);
10 }
11 
12 inline void getmiu(int n = 50005) {
13     miu[1] = 1;
14     for(int i = 2; i <= n; i++) {
15         if(!vis[i]) {
16             p[++top] = i;
17             miu[i] = -1;
18         }
19         for(int j = 1; j <= top && i * p[j] <= n; j++) {
20             vis[i * p[j]] = 1;
21             if(i % p[j] == 0) {
22                 break;
23             }
24             miu[i * p[j]] = -miu[i];
25         }
26     }
27     for(int i = 1; i <= n; i++) {
28         sum[i] = sum[i - 1] + miu[i];
29     }
30     return;
31 }
32 
33 inline int solve(int n, int m) {
34     n /= k;
35     m /= k;
36     int c = m > n ? n : m;
37     int ans = 0;
38     /*for(int i = 1; i <= c; i++) {
39         //ans += miu[i] * F(i, n, m);
40         ans += miu[i] * (n / i) * (m /i);
41     }*/
42     for(int i = 1, j; i <= c; i = j + 1) { // [i, j]
43         j = std::min(n / (n / i), m / (m / i));
44         ans += (n / i) * (m / i) * (sum[j] - sum[i - 1]);
45     }
46     return ans;
47 }
48 
49 int main() {
50     int n, a, b, c, d;
51     getmiu();
52     scanf("%d", &n);
53     while(n--) {
54         scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
55         int ans = solve(b, d) - solve(b, c - 1) - solve(a - 1, d) + solve(a - 1, c - 1);
56         printf("%d\n", ans);
57     }
58 
59     return 0;
60 }
AC代码

 

posted @ 2018-08-02 17:19  garage  阅读(95)  评论(0编辑  收藏  举报