注意:本博客并非写给欧拉函数的初学者,而是为已经学会欧拉函数的OIer们提供一点总结。
题型一:求解单个数的欧拉函数值
模板题:POJ2407
方法:根据公式求解
根据欧拉函数的通项公式
φ
(
x
)
=
x
∏
i
=
1
n
(
1
−
1
p
i
)
\varphi(x)=x\prod_{i=1}^{n}(1-\frac{1}{p_{i}})
φ(x)=xi=1∏n(1−pi1)通过对数x进行类似质因数分解的操作完成单个欧拉函数值的计算。复杂度
Θ
(
x
)
\Theta(\sqrt{x})
Θ(x)。
代码:
#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;
}
题型二:求解一段数的欧拉函数值
模板题:HDU2824
方法一:利用埃拉托斯特尼筛求解一段数的欧拉函数值
当需要求解一段连续的几十万个数的欧拉函数时,使用第一种方法基本上就TLE了,我们利用埃氏筛在用素数p筛除其他数的同时,计算p对每一个被筛的数欧拉函数的贡献,复杂度是 Θ ( n l o g l o g n ) \Theta(nloglogn) Θ(nloglogn)。具体见代码。
#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)
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)
Θ(nloglogn)的。而是通过欧拉函数的性质(也可以理解是通项)来推出被筛数的欧拉函数。具体如下:
在欧拉筛中,当
p
j
p_{j}
pj整除
i
i
i时,
φ
(
p
j
∗
i
)
=
φ
(
i
)
∗
p
j
\varphi(p_{j}*i)=\varphi(i)*p_{j}
φ(pj∗i)=φ(i)∗pj
否则
φ
(
p
j
∗
i
)
=
φ
(
i
)
∗
(
p
j
−
1
)
\varphi(p_{j}*i)=\varphi(i)*(p_{j}-1)
φ(pj∗i)=φ(i)∗(pj−1)。
证明:
以下是一种我认为相当直观好理解的证明方法(自己想到的):
1.当
p
j
p_{j}
pj整除
i
i
i时,也就是
i
i
i含有
p
j
p_{j}
pj这个素因子,那么被筛数
a
=
i
∗
p
j
a=i*p_{j}
a=i∗pj的不同的素因子个数完全没有改变,在欧拉函数的通项公式中:
φ
(
x
)
=
x
∏
i
=
1
n
(
1
−
1
p
i
)
\varphi(x)=x\prod_{i=1}^{n}(1-\frac{1}{p_{i}})
φ(x)=xi=1∏n(1−pi1)
我们可以知道
a
a
a与
i
i
i的
∏
i
=
1
n
(
1
−
1
p
i
)
\prod_{i=1}^{n}(1-\frac{1}{p_{i}})
i=1∏n(1−pi1)这一部分是一模一样的。因此
φ
(
a
)
\varphi(a)
φ(a)的值与
φ
(
i
)
\varphi(i)
φ(i)的值区别只在于
x
x
x,而
a
=
i
∗
p
j
a=i*p_{j}
a=i∗pj,所以差的是一个x中的
p
j
p_{j}
pj,因此当
p
j
p_{j}
pj整除
i
i
i时,
φ
(
p
j
∗
i
)
=
φ
(
i
)
∗
p
j
\varphi(p_{j}*i)=\varphi(i)*p_{j}
φ(pj∗i)=φ(i)∗pj。
2.根据同样的思路,当
p
j
p_{j}
pj不整除
i
i
i时,也就是
i
i
i不含有
p
j
p_{j}
pj这个素因子。
那么被筛数
a
=
i
∗
p
j
a=i*p_{j}
a=i∗pj的不同的素因子个数只改变增加了一个,那就是
p
j
p_{j}
pj.
因此在
φ
(
a
)
=
φ
(
i
)
∗
p
j
\varphi(a)=\varphi(i)*p_{j}
φ(a)=φ(i)∗pj的基础上,还要再乘上一个值,那就是
p
j
−
1
p
j
\frac{p_{j}-1}{p_{j}}
pjpj−1.
即
φ
(
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)
φ(a)=φ(i)∗pj∗pjpj−1=φ(i)∗(pj−1)。证毕。
复杂度
Θ
(
n
)
\Theta(n)
Θ(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;
}
觉得写得好的话,就点个赞让我知道一下呗~