**
注意:本博客并非写给欧拉函数的初学者,而是为已经学会欧拉函数的OIer们提供一点总结。
**
方法一:根据公式求解单个数的欧拉函数值
根据欧拉函数的通项公式
通过对数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对每一个被筛的数欧拉函数的贡献,复杂度是
。
模板题: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;
}
方法三:利用欧拉筛求解一段数的欧拉函数值
同样也是在筛除其他数的同时算出被筛数的欧拉函数,但并不是像埃氏筛那样子使用每一个素数计算对被筛数的贡献——那样复杂度是
的。而是通过欧拉函数的性质(也可以理解是通项)来推出被筛数的欧拉函数。具体如下:
在欧拉筛中,当
整除
时,
否则
。
证明:
以下是一种我认为相当直观好理解的证明方法(自己想到的):
1.当
整除
时,也就是
含有
这个素因子,那么被筛数
的不同的素因子个数完全没有改变,在欧拉函数的通项公式中:
我们可以知道
与
的
这一部分是一模一样的。因此
的值与
的值区别只在于
,而
,所以差的是一个
,因此当
整除
时,
。
2.根据同样的思路,当
不整除
时,也就是
不含有
这个素因子。
那么被筛数
的不同的素因子个数只改变增加了一个,那就是
.
因此在
的基础上,还要再乘上一个值,那就是
.
即
。证毕。
复杂度
。
模板题同上:
#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;
}
例题留坑待填···