zoj3435(莫比乌斯反演)

题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3435

题意: 给出一个三维坐标 (x, y, z), 问该点与 (1, 1, 1) 组成的长方体中有多少条经过点 (1, 1, 1) 的直线 .

思路:  显然本题的难点是线段不能重复计算, 比如 (1, 1, 1) 到 (2, 2, 2) 和 (1, 1, 1) 到 (4, 4, 4) 同一条直线 .

为了计算方便, 我们先将这个问题替换成它的等价问题: 求 (0, 0, 0), (x - 1, y - 1 , z - 1) 组成的长方体中有多少条经过 (0, 0, 0) 的直线 .

通过画图可以发现当且仅当点 (j, k, l) 满足 gcd(j, k, l) = 1 时累计直线 (0, 0, 0), (j, k, l) 刚好能累加到所有直线并且不会重复计算 .

然后我们将满足条件的直线分为 3 部分:

1. 从 (0, 0, 0) 出发的 3 条棱;

2. 从 (0, 0, 0) 出发的 3 个表面;

3. 从 (0, 0, 0) 出发到长方体内部的直线;

对于 1, 显然满足条件的直线为 3 条;

对于 2, 满足条件的直线数目为 gcd(j, k) = 1 的直线数目加 gcd(j, l) = 1 的直线数目加 gcd(k, l) = 1 的直线数目, 其中 1 <= j <= x -1, 1 <= k <= y - 1, 1 <= l <= z -1;

对于 3, 满足条件的直线数目为 gcd(j, k, l) = 1 的直线数目, 其中 1 <= j <= x -1, 1 <= k <= y - 1, 1 <= l <= z -1;

显然对于上面的计算部分都可以用莫比乌斯反演在线性时间内完成 .

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define ll long long
 5 using namespace std;
 6 
 7 const int MAXN = 1e6 + 10;
 8 
 9 bool check[MAXN];
10 int prime[MAXN], mu[MAXN], sum[MAXN];
11 
12 void Moblus(void){
13     memset(check, false, sizeof(check));
14     int tot = 0;
15     mu[1] = 1;
16     sum[1] = 1;
17     for(int i = 2; i < MAXN; i++){
18         if(!check[i]){
19             prime[tot++] = i;
20             mu[i] = -1;
21         }
22         for(int j = 0; j < tot && prime[j] * i < MAXN; j++){
23             check[prime[j] * i] = true;
24             if(i % prime[j] == 0){
25                 mu[i * prime[j]] = 0;
26                 break;
27             }else mu[i * prime[j]] = -mu[i];
28         }
29         sum[i] = sum[i - 1] + mu[i];
30     }
31 }
32 
33 ll solve(int x, int y){
34     ll ans = 0;
35     if(x > y) swap(x, y);
36     for(int i = 1, la = 0; i <= x; i = la + 1){
37         la = min(x / (x / i), y / (y / i));
38         ans += (ll)(sum[la] - sum[i - 1]) * (x / i) * (y / i);
39     }
40     return ans;
41 }
42 
43 ll solve(int x, int y, int z){
44     ll ans = 0;
45     if(x > y) swap(x, y);
46     if(x > z) swap(x, z);
47     for(int i = 1, la = 0; i <= x; i = la + 1){
48         la = min(min(x / (x / i), y / (y / i)), z / (z / i));
49         ans += (ll)(sum[la] - sum[i - 1]) * (x / i) * (y / i) * (z / i);
50     }
51     return ans;
52 }
53 
54 int main(void){
55     Moblus();
56     int x, y, z;
57     while(~scanf("%d%d%d", &x, &y, &z)){
58         x--;
59         y--;
60         z--;
61         ll ans = 3;
62         ans += solve(x, y);
63         ans += solve(x, z);
64         ans += solve(y, z);
65         ans += solve(x, y, z);
66         printf("%lld
", ans);
67     }
68     return 0;
69 }
View Code
原文地址:https://www.cnblogs.com/geloutingyu/p/7250128.html