AcWing算法提高课-5.5.1可见的点

宣传一下 算法提高课整理

CSDN个人主页:更好的阅读体验

Start

原题链接
题目描述

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

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

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

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

输入格式

第一行包含整数 C C C,表示共有 C C C 组测试数据。

每组测试数据占一行,包含一个整数 N N N

输出格式

每组测试数据的输出占据一行。

应包括:测试数据的编号(从 1 1 1 开始),该组测试数据对应的 N N N 以及可见点的数量。

同行数据之间用空格隔开。

数据范围

1 ≤ N , C ≤ 1000 1 \le N,C \le 1000 1N,C1000

输入样例:
4
2
4
5
231
输出样例:
1 2 5
2 4 13
3 5 21
4 231 32549

思路

首先我们考虑在 ( 0 , 0 ) (0,0) (0,0) 位置上不能看见点 ( x , y ) (x,y) (x,y) 的条件为 gcd ⁡ ( x , y ) ≠ 1 \gcd(x,y) \not= 1 gcd(x,y)=1

所以整个图能看见的点的数量为

∑ i = 0 n ∑ j = 0 n [ gcd ⁡ ( i , j ) = 1 ] \sum_{i=0}^{n}\sum_{j=0}^{n}[\gcd(i,j) = 1] i=0nj=0n[gcd(i,j)=1]

由于整个图形关于 y = x y=x y=x 对称,所以我们只要统计上面一半(或下面)然后把答案 × 2 + 1 \times 2+1 ×2+1即可。

所以上面的式子化简为:

2 × ∑ i = 0 n ∑ j = 0 i [ gcd ⁡ ( i , j ) = 1 ] + 1 2 \times \sum_{i=0}^{n}\sum_{j=0}^{i}[\gcd(i,j)=1] + 1 2×i=0nj=0i[gcd(i,j)=1]+1

这里我们引入 欧拉函数【 φ ( n ) \varphi(n) φ(n) 的概念:

对于正整数n,欧拉函数是小于n的正整数中与n互质的数的数目.

即:对于 n ∈ N ∗ n \in \mathbb{N^*} nN φ ( n ) = ∑ i = 1 n [ gcd ⁡ ( i , n ) = 1 ] \varphi(n)=\sum_{i=1}^{n}[\gcd(i,n)=1] φ(n)=i=1n[gcd(i,n)=1]

所以答案可简化为:

2 × ∑ i = 0 n φ ( i ) + 1 2 \times \sum_{i=0}^{n}\varphi(i) +1 2×i=0nφ(i)+1

我们线性预处理 1 ∼ 1 0 3 1 \sim 10^3 1103 的欧拉函数,然后直接进行计算即可。

算法时间复杂度 O ( 1 0 3 + ∑ N ) O(10^3+\sum N) O(103+N)

线性筛欧拉函数时间复杂度为 O ( 1 0 3 ) O(10^3) O(103);

对于每一个输入的 N N N,时间复杂度都是 O ( N ) O(N) O(N)

因次总时间复杂度为 O ( 1 0 3 + ∑ N ) O(10^3+\sum N) O(103+N)

最后不要忘了开 long long


AC Code

C + + \text{C}++ C++

#include <iostream>
#define int long long

using namespace std;

const int N = 1010;

int n, c;
int primes[N], cnt;
int euler[N];
bool st[N];

void get_eulers(int n)
{
    euler[1] = 1;
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            euler[i] = i - 1;
        }
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
            int t = primes[j] * i;
            st[t] = true;
            if (i % primes[j] == 0)
            {
                euler[t] = euler[i] * primes[j];
                break;
            }
            euler[t] = euler[i] * (primes[j] - 1);
        }
    }
}

main()
{
    scanf("%lld", &c);
    get_eulers(N - 1);
    
    for (int _ = 1; _ <= c; _ ++ )
    {
        scanf("%lld", &n);
        int res = 0;
        for (int i = 0; i <= n; i ++ )
            res += euler[i];
        printf("%lld %lld %lld\n", _, n, 2 * res + 1);
    }
    
    return 0;
}


228aa7bed3e021faf24cf8560d3e47bb.gif

最后,如果觉得对您有帮助的话,点个赞再走吧!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星河依旧长明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值