1. 程式人生 > 其它 >visible lattice points 尤拉函式 線性篩

visible lattice points 尤拉函式 線性篩

技術標籤:演算法

題目描述

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.

在這裡插入圖片描述

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

大致題意

求位於(0,0)時,向四周看能看到多少個點。一個點能被看到,當且僅當連線它和(0,0)的線段上沒有其他點。

分析

除了(1,0),(0,1),(1,1)三個點外,一個點能被看到當且僅當x≠y且gcd(x,y)=1,即求x與y互質的點的數量,此時想到尤拉函式。
因為能被看到的點關於(0,0)(N,N)直線對稱,所以只要求上半部分每個y(2~N)的尤拉函式之和ans。可得總數為2*ans+3。

程式碼

#include<cstdio>//使用scanf和printf的標頭檔案
#include<cstring>//使用C風格字串函式的標頭檔案
#include<algorithm>//使用演算法庫的標頭檔案,max,min,swap,sort等
#include<iostream>//使用cin,cout的標頭檔案
#include<string>//使用string時的標頭檔案
#include<vector>//使用vector時的標頭檔案
#include<cmath>//數學函式的標頭檔案
#include<set>//使用set的標頭檔案
using namespace std;
#define ll unsigned long long
ll n,c,size,ans;
ll v[1010],prime[1010],phi[1010],m;//phi[i] i的尤拉函式值 
void eular(){
	//初始化陣列 
	memset(v,0,sizeof(v));
	memset(phi,0,sizeof(phi));
	memset(prime,0,sizeof(prime));

	m=0;//記錄素數數量 
	
	//線性篩法 求出2~1000中每個數的尤拉函式 
	for(ll a=2;a<=1000;a++){
		if(!v[a]){				//篩素數,同時得此素數的尤拉函式 
			v[a]=a;
			prime[++m]=a;
			phi[a]=a-1;
		}
		for(ll b=1;b<=m;b++){			//遍歷 小於等於 a 的素數 
			if(prime[b]>v[a]||prime[b]*a>1000)	break;	//如果此素數prime[b]大於 a 的最小質因子 或者 prime[b]*a > 上限1000,則停止遍歷 
			v[a*prime[b]]=prime[b];					//每個倍數用最小質因子prime[b]標記 
			phi[a*prime[b]]=phi[a]*(a%prime[b]? prime[b]-1:prime[b]);	//根據尤拉函式性質4、5 
		}
	}
}
int main(){
	cin>>c;
	//預處理 2~1000的尤拉函式phi[i] 
	eular();
	for(ll i=1;i<=c;i++){
		ans=0;
		cin>>n;
		for(ll j=2;j<=n;j++){
			ans+=phi[j];
		}
		printf("%llu %llu %llu\n",i,n,2*ans+3);//看題目可推出公式 2*(2~n尤拉函式之和)+3 
	}
	return 0;
}

線性篩法

每個合數必有一個最小素因子,用這個最小素因子把合數篩掉,避免同個合數被多個因子重複篩。

你可以找到更多關於的資訊 線性篩法過程 here.