引入:互质的概念:如果 正整数 a 与b 之间只有一个公约数1 则称a与 b 互为质数。
欧拉函数的定义: 1-N 中 与N 互质的数的个数 记作 Phi(N)
在算数基本定理中任意自然数能进行质因数拆分,那么由容斥原理:
1>假设N 的质因子由p1……p(k) 一共有k个;
2>从1到N 去掉p1……p(k)的所有倍数;p(i)的倍数的个数是 N/p(i)下取整;
3>发现有多去除的,所以每次加上所有p(i)*p(j)的倍数
4>以此类推 发现有多加的 减去所有 三个质数乘积的倍数 ~~~加上所有四个质数乘积的倍数。
这恰好就是欧拉函数展开的结果,不得不佩服欧拉斯人
从而得到 欧拉函数 Phi(N)=N*(1-1/p1)*(1-1/p2)*(1-1/p3)*(1-p4)*(1-p5)……*(1-1/pk);
观察这个式子可知欧拉函数的值只与N 和N 的质因子种类数有关,而N 是确定的,因此,只要找出N的质因子的种类数,就容易得出欧拉函数的值。
这也是我们求欧拉函数值的时候重点关注的一点。
//给你n个正整数ai 是求出每个数的欧拉函数。
//示例为试除法求欧拉函数
#include
using namespace std;
#define int long long
int tt;
signed main(){
cin>>tt;
while(tt--)
{
int a;
cin>>a;
int res=a;
for(int i=2;i<=a/i;i++){
if(a%i==0) res=res/i*(i-1);
while(a%i==0) a/=i;
}
if(a) res=res/i*(i-1);
cout<
由欧拉函数性质我们可以借助线性筛出质数的同时求出欧拉函数的值。
证明一:如果x是质数,那么phi(x)=x-1。
如果x是质数,那么除了它自身以外的所有小于x的自然数都与x互质个数为 x-1.
证明二: 如果pj是小于x的一个质因子,那么phi(x*pj)=phi(x)*pj 。
首先我们假设 x的质因子分别是 p1、p2、....pk 那么因为 pj 是 x的一个质因子,可知pj 一定为 p1-pk 中的某一个质因子,那么把(x*pj)看作一个整体 由欧拉函数的定义得知 phi(x*pj)=(x*pj)*(1-1/p1)*(1-1/p2)*()........*(1-1/pk)(因为pj是p1到pk中的某一个质因子,所以x*pj 与x 的质因子种类是相同的) 那么进一步可得:
phi(x * pj)= pj *(x * (1-1/p1) * (1-1/p2) * ()........* (1-1 / pk) )=pj * phi(x);
后面这一部分恰好就是phi(x);
得证。
证明三:如果pj不是小于x 的一个质因子那么phi(x*pj)=phi(x)*(pj-1);
同样的我们首先假设一下 x的质因子分别是p1 、p2、p3.....pk; 那么把(x*pj)看作一个整体
他的质因子数只比原来的x多了一个就是 pj。
那么由欧拉函数可得 phi(x*pj)=(x*pj)*(1-1/p1)*(1-1/p2)*(1-1/p3)*......*(1-1/pk)*(1-1/pj);
那么整理后可得 phi(x*pj)=(1-1/p1)*(1-1/p2)*(1-1/p3)*......*(1-1/pk))*(1-1/pj)*pj;
前面这一部分就是phi(x);
也就是 phi(x*pj)=phi(x)*(pj-1);
有了上述这三条性质,我们就比较容易的利用线性筛法在O(N)的时间复杂度下,求出从一到N的所有欧拉函数的值了。
下面给出利用线性筛法来求欧拉函数值的代码:
eg:给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和。n<1e6;
#include
using namespace std;
#define int long long //脚注:这个代码为了避免暴int 使用了#define int long long 这一宏定义
const int N=1e6+10;
bool st[N];
int cnt,primes[N];//存储质数的数组
int phi[N];//欧拉函数数组
int sum_elur(int n){
for(int i=2;i<=n;i++){
if(!st[i]) primes[cnt++]=i,phi[i]=i-1;//如果没被筛过说明是质数利用证明一
//枚举质数的倍数从2开始,在筛指数是顺带求一下欧拉函数值
for(int j=0;primes[j]*i<=n;j++){
st[primes[j]*i]=1;
if(i%primes[j]==0)
{
//如果说pj是i 的一个质因子 利用证明二求欧拉函数的值。
phi[primes[j]*i]=phi[i]*primes[j];
break;//primes[j] 已经是i 的最小质因子了,不再进行枚举了。
}
phi[primes[j]*i]=phi[i]*(primes[j]-1);//如果pj 不是i 的一个质因子,利用证明三。
}
}
int res=0;
for(int i=1;i<=n;i++){
res+=phi[i];//累加求和
}
return res+1;//因为省去了初始化 phi [1]=1,在答案上加1就行;
}
signed main(){
int n;
cin>>n;
cout<
放几个个例题比较好:
eg1.可见的点
在一个平面直角坐标系的第一象限内,如果一个点 (x,y)(x,y) 与原点 (0,0)(0,0) 的连线中没有通过其他任何点,则称该点在原点处是可见的。
例如,点 (4,2)(4,2) 就是不可见的,因为它与原点的连线会通过点 (2,1)(2,1)。
部分可见点与原点的连线如下图所示:
编写一个程序,计算给定整数 N 的情况下,满足 0≤x,y≤N 的可见点 (x,y) 的数量(可见点不包括原点)。
输入格式
第一行包含整数 C,表示共有 C 组测试数据。
每组测试数据占一行,包含一个整数 N。
输出格式
每组测试数据的输出占据一行。
应包括:测试数据的编号(从 1 开始),该组测试数据对应的 N以及可见点的数量。
同行数据之间用空格隔开。
数据范围
1≤N,C≤1000
输入样例:
4
2
4
5
231
输出样例:
1 2 5
2 4 13
3 5 21
4 231 32549
思路:题意的理解,题目实际上要求的是直线第一次经过的点的个数,那么对于 一个点p (x,y)如果说 x ,y 有最大公约数,设d=gcd(x,y),则有(x/d,y/d)也在,也就是说,如果x ,y 有最小公倍数的话 那么这个点其实就和 (x/d,y/d)是等价的,是不会使得答案增加的,那么其实就是求所有没有最小公倍数的二元组
我们预处理出所有小于y的且与y 互质的元素的个数,最后求和,这不就是欧拉函数吗,问题完美解决了
代码:
送你一堆样例,增强数感的版本^_^
#include
using namespace std;
int n,c;
const int N=2e5+10;
int primes[N],cnt;
bool st[N];
int phi[N];
void init(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
primes[cnt++]=i;
phi[i]=i-1;
}
for(int j=0;primes[j]>c;
for(int i=1;i<=c;i++){
int n;
cin>>n;
int ans=0;
for(int j=2;j<=n;j++){
ans+=phi[j];
}
cout<
eg2:最大公约数
给定整数 N,求 1≤x,y≤N 且 GCD(x,y) 为素数的数对 (x,y) 有多少对。
GCD(x,y)即求 x,y的最大公约数。
输入格式
输入一个整数 N。
输出格式
输出一个整数,表示满足条件的数对数量。
数据范围
1≤N≤1e7
输入样例:
4
输出样例:
4
思路:还是从这个式子入手 gcd(x,y)的值是一个素数,假设 gcd(x,y)=p ,那么p|x 且 p|y,因为是最大公约数,所以必有x/p==1 or y/p==1,而我们知道 gcd(x/p,y/p)==gcd(x/p,1) or gcd(1,y/p)=1,这不就是互质的定义吗,我可以固定y /p求所有小于y /p的的与y/p 互质的数的个数,这不就是欧拉函数吗,我们只需枚举所有小于n 的所有质数,然后像第一题一样求欧拉函数的和就行
代码:
#include
using namespace std;
#define LL long long
const int N=1e7+10;
int primes[N],cnt;
int st[N];
int phi[N];
long long sum[N];
void init(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
primes[cnt++]=i;
phi[i]=i-1;
}
for(int j=0;primes[j]<=n/i;j++){
st[primes[j]*i]=1;
if(i%primes[j]==0){
phi[primes[j]*i]=phi[i]*primes[j];
break;
}
phi[i*primes[j]]=phi[i]*(primes[j]-1);
}
}
for(int i=2;i<=n;i++){
sum[i]=sum[i-1]+phi[i];
}
}
signed main(){
int n;
cin>>n;
init(n);
LL ans=0;
for(int i=0;i