目录
1. POJ3090 Visible Lattice Points
一. 欧拉函数概述
1. 基本概念
我们知道互质是指两个正整数只有一个公因数1时,它们的关系叫做互质。欧拉函数就是对于一个正整数n, 求1~n中跟n互质的数有多少个。
2. 公式推导
举例一个数字12。12分解为最小的质因数为 12 = 2*2*3 ,也就是说2和3的倍数跟12都不是互质的,那么跟12不互质的有 12/2 + 12/3个,但是这里重复了12/6个 所以互质数目N = 12 - 12/2 - 12/3 + 12/6 = 12(1 - 1/2)(1 - 1/3)。由上述规律我们可以得到欧拉函数的公式:
euler(x) = x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…(1-1/pn)
其中p1,p2……pn为x的所有质因数,且x是不为0的整数;注意euler(1)=1,即唯一和1互质的数就是1本身。
二. 代码实现
1. 直接求解
int Euler(int a)
{
int ans = a;//累积ans = n*(1-1/p1)(1-1/p2)....初始化为n
for(int j = 2;j*j<=a;j++){//求所有质因数
if(a%j==0){//找到一个质因数
ans = ans/j*(j - 1);//累乘(先除后乘防止溢出)
while(a%j==0)a/=j;//把这个质因数除干净
}
}
if(a>1)ans = ans/a*(a - 1);//如果最后不等于1说明剩下一个质因数累乘
return ans;返回结果
}
2. 打表求解
int num[maxn];
memset(num,0,sizeof(num));
num[1] = 1;//1的互质数为1
void Euler(int a)
{
for(int i = 2;i<=a;i++){
if(!num[i]){//这个数没标记过说明这个数是一些数字的质因数
for(int j = i;j<=a;j+=i){//他的倍数都是以他为质因数
if(!num[j])num[j] = j;
num[j] = num[j]/i*(i-1);//累乘
}
}
}
}
3. 循环求解
int n;
scanf("%d",&n);
for(int i = 2;i<n;i++){
if(n%i==0&&!judge[i]){
for(int j = i;j<=n;j+=i)judge[j] = 1;
}
}
int sum = 0;
for(int i = 1;i<n;i++)
{
if(!judge[i])sum++;
}
printf("%d\n",sum);
三. 例题分析
1. POJ3090 Visible Lattice Points
在n*n的平面内找有几个点满足,与原点连线不经过其他任何一个点?
所有不经过其他点的连线,在该点xy相互是互质的。所以首先求出所有1~n的欧拉函数值,注意这里行和列是分别计算的!累积起所有的1~k的欧拉函数值即为答案,但是要注意(0,1)和(1,0)这两个特殊值要加上。
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long int LL;
const int maxn = 1000 + 5;
LL num[maxn];//储存每个数字1~k的欧拉函数值
LL Sum[maxn];//求前缀和预处理一下子
int n;
void Euler(int n)//欧拉函数打表
{
num[1] = 1;
for(int i = 2;i<=maxn;i++){
if(!num[i]){
for(int j = i;j<=maxn;j+=i){
if(!num[j])num[j] = j;
num[j] = num[j]/i*(i - 1);
}
}
}
}
void pretation()//预处理
{
Sum[1] = 1;
for(int i = 2;i<=maxn;i++){
Sum[i] = Sum[i - 1] + num[i];
}
}
int main()
{
memset(num,0,sizeof(num));
memset(Sum,0,sizeof(Sum));
Euler(maxn);
pretation();
int T;
scanf("%d",&T);
int t = 0;
while(T--){
t++;
scanf("%d",&n);
printf("%d %d %lld\n",t,n,Sum[n]*2 - 1 +2);//行列分别求所以sum[n]*2
}
return 0; //-1是因为(1,1)求了两遍,减去一个
} //+2是因为加上两个特殊点