欧拉函数求法总结及相关例题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TengWan_Alunl/article/details/83413061

**

注意:本博客并非写给欧拉函数的初学者,而是为已经学会欧拉函数的OIer们提供一点总结。

**


方法一:根据公式求解单个数的欧拉函数值

根据欧拉函数的通项公式 φ ( x ) = x i = 1 n ( 1 1 p i ) \varphi(x)=x\prod_{i=1}^{n}(1-\frac{1}{p_{i}}) 通过对数x进行类似质因数分解的操作完成单个欧拉函数值的计算。复杂度 Θ ( x ) \Theta(\sqrt{x})
模板题:POJ2407
代码:

#include<cstdio>
#include<algorithm>
#include<cmath>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
using namespace std;
int n;
int main(){
	while(scanf("%d",&n)==1&&n!=0){
		int ans=n,lim=sqrt(n);
		rep(i,2,lim) if(n%i==0){
			ans=ans/i*(i-1);//先除再乘,避免爆int
			while(n%i==0) n/=i; 
		}
		if(n>1) ans=ans/n*(n-1);
		printf("%d\n",ans);
	}
	return 0;
}

方法二:利用埃拉托斯特尼筛求解一段数的欧拉函数值

当需要求解一段连续的几十万个数的欧拉函数时,使用第一种方法基本上就TLE了,我们利用埃氏筛在用素数p筛除其他数的同时,计算p对每一个被筛的数欧拉函数的贡献,复杂度是 Θ ( n l o g l o g n ) \Theta(nloglogn)
模板题:HDU2824

#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
using namespace std;
const int inf=1e9+10,N=3e6+100;
int a,b,phi[N+1];
bool prime[N+1];//false为素数,true为合数。 
long long ans;
inline void Eratos_to_Euler(){
	rep(i,2,N) phi[i]=i;
	rep(i,2,N)
		if(prime[i]==false){
			prime[i]=i-1;
			for(int j=i;j<=N;j+=i) phi[j]=phi[j]/i*(i-1),prime[j]=true;
		}
}
int main(){
	Eratos_to_Euler();
	while(scanf("%d%d",&a,&b)==2){
		long long ans=0;
		rep(i,a,b) ans+=phi[i];
		printf("%lld\n",ans);
	}
	return 0;
}

方法三:利用欧拉筛求解一段数的欧拉函数值

同样也是在筛除其他数的同时算出被筛数的欧拉函数,但并不是像埃氏筛那样子使用每一个素数计算对被筛数的贡献——那样复杂度是 Θ ( n l o g l o g n ) \Theta(nloglogn) 的。而是通过欧拉函数的性质(也可以理解是通项)来推出被筛数的欧拉函数。具体如下:
在欧拉筛中,当 p j p_{j} 整除 i i 时, φ ( p j i ) = φ ( i ) p j \varphi(p_{j}*i)=\varphi(i)*p_{j}
否则 φ ( p j i ) = φ ( i ) ( p j 1 ) \varphi(p_{j}*i)=\varphi(i)*(p_{j}-1)
证明:
以下是一种我认为相当直观好理解的证明方法(自己想到的):
1.当 p j p_{j} 整除 i i 时,也就是 i i 含有 p j p_{j} 这个素因子,那么被筛数 a = i p j a=i*p_{j} 的不同的素因子个数完全没有改变,在欧拉函数的通项公式中:
φ ( x ) = x i = 1 n ( 1 1 p i ) \varphi(x)=x\prod_{i=1}^{n}(1-\frac{1}{p_{i}})
我们可以知道 a a i i i = 1 n ( 1 1 p i ) \prod_{i=1}^{n}(1-\frac{1}{p_{i}}) 这一部分是一模一样的。因此 φ ( a ) \varphi(a) 的值与 φ ( i ) \varphi(i) 的值区别只在于 x x ,而 a = i p j a=i*p_{j} ,所以差的是一个 p j p_{j} ,因此当 p j p_{j} 整除 i i 时, φ ( p j i ) = φ ( i ) p j \varphi(p_{j}*i)=\varphi(i)*p_{j}

2.根据同样的思路,当 p j p_{j} 不整除 i i 时,也就是 i i 不含有 p j p_{j} 这个素因子。
那么被筛数 a = i p j a=i*p_{j} 的不同的素因子个数只改变增加了一个,那就是 p j p_{j} .
因此在 φ ( a ) = φ ( i ) p j \varphi(a)=\varphi(i)*p_{j} 的基础上,还要再乘上一个值,那就是 p j 1 p j \frac{p_{j}-1}{p_{j}} .
φ ( a ) = φ ( i ) p j p j 1 p j = φ ( i ) ( p j 1 ) \varphi(a)=\varphi(i)*p_{j}*\frac{p_{j}-1}{p_{j}}=\varphi(i)*(p_{j}-1) 。证毕。

复杂度 Θ ( n ) \Theta(n)
模板题同上:

#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
using namespace std;
typedef long long ll;
const int inf=1e9+10,N=3e6+100;
int a,b,phi[N+1],v[N],prime[N],len;
inline void Euler_to_Euler(){
	rep(i,2,N){
		if(v[i]==0){
			v[i]=i;
			phi[i]=i-1;
			prime[++len]=i;
		}
		rep(j,1,len){
			if(prime[j]>N/i||prime[j]>v[i]) break;
			//最好写成 prime[j]>N/i,而不是i*prime[j]>N,避免爆int。
			v[i*prime[j]]=prime[j];//筛 
			if(i%prime[j]==0) phi[i*prime[j]]=phi[i]*prime[j];//计算欧拉函数 
				else phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}
int main(){
	Euler_to_Euler();
	while(scanf("%d%d",&a,&b)==2){
		long long ans=0;
		rep(i,a,b) ans+=phi[i];
		printf("%lld\n",ans);
	}
	return 0;
}

例题留坑待填···

猜你喜欢

转载自blog.csdn.net/TengWan_Alunl/article/details/83413061