跳转至

可见的点

原题链接

题目描述

在一个平面直角坐标系的第一象限内,如果一个点 (x,y) 与原点 (0,0) 的连线中没有通过其他任何点,则称该点在原点处是可见的。

例如,点 (4,2) 就是不可见的,因为它与原点的连线会通过点 (2,1)。

部分可见点与原点的连线如下图所示:

编写一个程序,计算给定整数 N 的情况下,满足 \(0≤x,y≤N\) 的可见点 (x,y) 的数量(可见点不包括原点)。


欧拉函数

对于一个点 \((x_0, y_0)\),与原点 \((0, 0)\),构成的直线方程为 \(y=\frac{y_0}{x_0}x\)。要使点是从原点出发,沿着这条直线第一个遇到的点 \((x, y)\),那必然是

\[ \begin{cases} x=\frac{x_0}{\gcd(x_0,y_0)}\\ y=\frac{y_0}{\gcd(x_0,y_0)} \end{cases} \]

即,\(\gcd(x,y)=1\)

同时可以发现可到达的点都是对于 \(y=x\) 对称。因此右下半区内,对于横坐标x,要找到范围 \([2,x)\) 与其互质的数的数量即为 \(\varphi(x)\)

因此 \(res=3+\sum_{i=2}^{n}\varphi(i)\)

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;

int n;
int primes[N], cnt;
bool st[N];
int phi[N];

void init(int n) {
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            primes[cnt++] = i;
            phi[i] = i - 1;
        }

        for (int j = 0; primes[j] <= n / i; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) {
                phi[primes[j] * i] = phi[i] * primes[j];
                break;
            }
            phi[primes[j] * i] = phi[i] * (primes[j] - 1);
        }
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);

    init(1000);

    cin >> n;
    for (int j = 1; j <= n; j++) {
        int x;
        cin >> x;
        int res = 3;
        for (int i = 2; i <= x; i++) res += phi[i] * 2;
        cout << j << ' ' << x << ' ' << res << endl;
    }

    return 0;
}
回到页面顶部