Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 7303 | Accepted: 4428 |
Description
A lattice point (x, y) 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 (x, y) 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 (x, y) with 0 ≤ x, y ≤ 5 with lines from the origin to the visible points.
![](https://i-blog.csdnimg.cn/blog_migrate/df136192ea0282d99ffbfa2d5d0c5330.png)
Write a program which, given a value for the size, N, computes the number of visible points (x, y) with 0 ≤ x, y ≤ 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
Source
- 2017计蒜客初赛的一道题可能是从这里来的吧
- 那个初赛里面有一个快速枚举同一条线段上的整数点的操作
- 当时惊为天人,其实还是自己太鶸~
- 那么对于这道题来说,不难发现一条射线的第一个点的坐标(x,y)==1
- 由于该题要求的所有射线都是自原点发出,所以后续的在射线上被第一个点遮挡的点坐标的gcd不为1,对应坐标是第一个点的k倍
- 不难想到用gcd:
int gcd(int x, int y){ return y?gcd(y,x%y):x; } int cal(int n){ int ans=0; for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(1==gcd(i,j)) ans++; return 2*ans + 3; }
- 方法没错,但是超时
- 接着看,这里gcd的操作等价于求1到n的欧拉函数:
int cal(int n){ int ans=0; for(int i=1;i<=n;i++) ans+=phi[i]; return ans*2 + 1; }
- 然后就A了呀
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
typedef long long LL ;
typedef unsigned long long ULL ;
const int maxn = 1000 + 10 ;
const int inf = 0x3f3f3f3f ;
const int npos = -1 ;
const int mod = 1e9 + 7 ;
const int mxx = 100 + 5 ;
const double eps = 1e-6 ;
int phi[maxn], minDiv[maxn], sum[maxn];
void genPhi(void){
for(int i=1;i<maxn;i++)
minDiv[i]=i;
for(int i=2;i*i<maxn;i++)
if(minDiv[i]==i)
for(int j=i*i;j<maxn;j+=i)
minDiv[j]=i;
phi[1]=1;
for(int i=2;i<maxn;i++){
phi[i]=phi[i/minDiv[i]];
if((i/minDiv[i]) % minDiv[i] == 0)
phi[i]*=minDiv[i];
else
phi[i]*=minDiv[i]-1;
}
}
int cal(int n){
int ans=0;
for(int i=1;i<=n;i++)
ans+=phi[i];
return ans*2 + 1;
}
// int gcd(int x, int y){
// return y?gcd(y,x%y):x;
// }
// int cal(int n){
// int ans=0;
// for(int i=1;i<=n;i++)
// for(int j=1;j<i;j++)
// if(1==gcd(i,j))
// ans++;
// return 2*ans + 3;
// }
int T, kase, n;
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
genPhi();
while(~scanf("%d",&T)){
for(kase=1;kase<=T;kase++){
scanf("%d",&n);
printf("%d %d %d\n",kase,n,cal(n));
}
}
return 0;
}