暑假训练6之Visible Lattice Points(欧拉函数)

本文介绍了一种利用欧拉函数解决特定几何问题的方法——计算第一象限内可见的格点数量。通过分析不同大小网格中可见点的规律,并结合欧拉函数的性质,给出了一种高效的算法实现。

Description

A lattice point (xy) in the first quadrant (x and y are integers greater than or equal to 0), other than the origin, is visible from the origin if the line from (0, 0) to (xy) does not pass through any other lattice point. For example, the point (4, 2) is not visible since the line from the origin passes through (2, 1). The figure below shows the points (xy) with 0 ≤ xy ≤ 5 with lines from the origin to the visible points.

Write a program which, given a value for the size, N, computes the number of visible points (xy) with 0 ≤ xy ≤ N.

Input

The first line of input contains a single integer C (1 ≤ C ≤ 1000) which is the number of datasets that follow.

Each dataset consists of a single line of input containing a single integer N (1 ≤ N ≤ 1000), which is the size.

Output

For each dataset, there is to be one line of output consisting of: the dataset number starting at 1, a single space, the size, a single space and the number of visible points for that size.

Sample Input

4
2
4
5
231

Sample Output

1 2 5
2 4 13
3 5 21

4 231 32549

 

首先,题目主要是求从0,0能看到的点的个数。

先考虑只有1×1的时候,三个点,根据图明显看出,只需要计算下三角,结果=下三角的个数×2再加1(斜率为1的点)。

那么我们只需要计算斜率从0到1之间的个数就行了,不包括1,包括0.结果设为sum,那么最终就是2*sum+1.

 

1×1只有一个斜率为0的

2×2斜率有0,1/2(0已经算过了,以后不再算了),其实就多了一个斜率为1/2的。

3×3的时候,有1/3,2/3两个,比以前多了2个

4×4的时候,有1/4,2/4(1/2已经有过了),3/4,所以也是2个

5×5的时候,有1/5,2/5,3/5,4/5,之前都没有,所以多了4个

6×6得到时候,有1/6,2/6(1/3已经有了),3/6(1/2已经有了),4/6(2/3已经有了),5/6,所以只剩2个。

从上面可以发现一个规律,对于n×n,可以从0,0连接到(n,0)到(n,n)上,斜率将会是1/n,2/n......(n-1)/n;

凡是分子和分母能够约分的,也就是有公约数,前面都已经有过了。所以每次添加的个数就是分子和分母互质的个数。

那么问题就转换为,对于一个数n,求小于n的于n互质的数的个数,这不就是欧拉函数么?

//题意:从原点看第一象限里的所有点,能直接看到的点的数目是多少(不包含原点)
//经过一系列的找规律发现:最后就是欧拉函数的相关问题 

#include<bits/stdc++.h>
using namespace std;
int p[10005],phi[10005],k=1;
bool vis[10005];
int main()
{
    int n,T;//vis[i]=false为素数
    vis[1]=true;//1 不是素数
    phi[1]=1;//1的欧拉函数值为1
    for(int i=2;i<=1005;i++)
    {
        if(!vis[i])//若是素数
        {
            p[k++]=i;
            phi[i]=i-1;//素数的欧拉函数值为i-1
        }
        for(int j=1;j<=k&&i*p[j]<=1005;j++)
        {
            vis[i*p[j]]=true;//不是素数
            if(i%p[j]==0)
            {
                phi[i*p[j]]=phi[i]*p[j];//定理
                break;
            }
            else
                phi[i*p[j]]=phi[i]*(p[j]-1);
        }
    }
    cin>>T;
    for(int j=1;j<=T;j++)
    {
        cin>>n;
        int ans=0;
        for(int i=1;i<=n;i++)
            ans+=phi[i];
        printf("%d %d %d\n",j,n,ans*2+1);
    }
    return 0;
}



#include<bits/stdc++.h>
using namespace std;
int p[10005],phi[10005],k=1;
bool vis[10005];
int main()
{
    int n,T;//vis[i]=false为素数
    vis[1]=true;//1 不是素数
    phi[1]=1;//1的欧拉函数值为1
    for(int i=2;i<=1005;i++)
    {
        if(!vis[i])//若是素数
        {
            p[k++]=i;
            phi[i]=i-1;//素数的欧拉函数值为i-1
        }
        for(int j=1;j<=k&&i*p[j]<=1005;j++)
        {
            vis[i*p[j]]=true;//不是素数
            if(i%p[j]==0)
            {
                phi[i*p[j]]=phi[i]*p[j];//定理
                break;
            }
            else
                phi[i*p[j]]=phi[i]*(p[j]-1);
        }
    }
    cin>>T;
    for(int j=1;j<=T;j++)
    {
        cin>>n;
        int ans=0;
        for(int i=1;i<=n;i++)
            ans+=phi[i];
        printf("%d %d %d\n",j,n,ans*2+1);
    }
    return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值