如果 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,则说明两个数互质。即如果两个数最大公约数是1,那么这两个数就是互质的。
比如14和15
14的约数:1,2,7,14
15的约数:1,3,5,15
二者的最大公约数是1,即 g c d ( 14 , 15 ) = 1 gcd(14,15)=1 gcd(14,15)=1,所以14和15互质。
对于一个数 n n n,从 1 1 1到 n n n中,与 n n n互质的个数记作 Φ ( n ) \Phi (n) Φ(n),那么这个 Φ ( n ) \Phi (n) Φ(n)就i被称作欧拉函数。
比如:
Φ ( 6 ) = 2 \Phi (6)=2 Φ(6)=2
因为,
1,2,3,4,5,6中与6互质的是1和5。
根据算数基本定理,该合数可以写成有限个质数的乘积,即:
n = p 1 k ∗ p 2 m ∗ p 3 x . . . p n w n=p_1^k*p_2^m*p_3^x...p_n^w n=p1k∗p2m∗p3x...pnw
那么 Φ ( n ) = n ∗ ( p 1 − 1 p 1 ) ∗ ( p 2 − 1 p 2 ) ∗ . . . ∗ ( p n − 1 p n ) \Phi (n)=n*(\frac{p_1-1}{p_1})*(\frac{p_2-1}{p2})*...*(\frac{p_n-1}{p_n}) Φ(n)=n∗(p1p1−1)∗(p2p2−1)∗...∗(pnpn−1)
这就是著名的欧拉函数。
我们可以验证一下:
6 = 2 ∗ 3 6=2*3 6=2∗3
Φ ( 6 ) = 6 ∗ 2 − 1 2 ∗ 3 − 1 3 = 2 \Phi (6)=6*\frac{2-1}{2}*\frac{3-1}{3}=2 Φ(6)=6∗22−1∗33−1=2
特殊地,
如果一个数是质数,我假设这个质数是 p p p,质数的算数基本定理的表达式就是本身,所以
Φ ( p ) = p ∗ ( p − 1 p ) = p − 1 \Phi (p)=p*(\frac{p-1}{p})=p-1 Φ(p)=p∗(pp−1)=p−1
所以,如果一个数是质数,那么这个数 p p p:
Φ ( p ) = p − 1 \Phi (p)=p-1 Φ(p)=p−1
那么这个函数怎么证明呢?
我们继续向下看:
现在问题的关键其实就是质因数的分解,而质因数的分解,作者在前面的文章详细讲解过分解方式:
传送门:质因数的分解
#include
using namespace std;
long long phi(int a)
{
long long ans=a;
for(int i=2;i<=a/i;i++)
{
if(a%i==0)ans=ans*(i-1)/i;
while(a%i==0)a/=i;
}
if(a>1)ans=ans*(a-1)/a;
return ans;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int a;
scanf("%d",&a);
cout<<phi(a)<<endl;
}
return 0;
}
这道题的关键在于求出1到n之间的每个数字的欧拉函数之和,那么我们可以将1到n之间的数字分成三类:
Φ ( 1 ) = 1 \Phi (1)=1 Φ(1)=1
Φ ( 质数 ) = 质数 − 1 \Phi(质数)=质数-1 Φ(质数)=质数−1
合数的话,则需要分解质因数计算求解。
Φ ( n ) = n ∗ ( p 1 − 1 p 1 ) ∗ ( p 2 − 1 p 2 ) ∗ . . . ∗ ( p n − 1 p n ) \Phi (n)=n*(\frac{p_1-1}{p_1})*(\frac{p_2-1}{p2})*...*(\frac{p_n-1}{p_n}) Φ(n)=n∗(p1p1−1)∗(p2p2−1)∗...∗(pnpn−1)
涉及到数的筛选问题:
我们可以联想到之前所学的两种筛法:埃氏筛法和欧拉筛法
不了解这两个筛法的同学建议先去看一下作者之前的讲解:
埃氏筛法和欧拉筛法
我们这里采用欧拉筛法。
那么欧拉筛和欧拉函数怎么结合求呢?
首先我们根据欧拉函数公式:
Φ ( n ) = n ∗ ( p 1 − 1 p 1 ) ∗ ( p 2 − 1 p 2 ) ∗ . . . ∗ ( p n − 1 p n ) \Phi (n)=n*(\frac{p_1-1}{p_1})*(\frac{p_2-1}{p2})*...*(\frac{p_n-1}{p_n}) Φ(n)=n∗(p1p1−1)∗(p2p2−1)∗...∗(pnpn−1)
可以知道,结果只和质数本身有关,和他的指数是无关的。
另外,我们的欧拉筛是用来筛质数的,质数的欧拉函数也是很好求的,那么我们思考一下:
能否用质数的欧拉函数结果去求解合数的欧拉函数?
p r i m e s [ j ] primes[j] primes[j]是质数,所以我们的 Φ ( p r i m e s [ j ] ) = p r i m e s [ j ] − 1 \Phi(primes[j])=primes[j]-1 Φ(primes[j])=primes[j]−1
我们欧拉筛的第二层循环中,我们存在这样一个判断 i m o d p r i m e s [ j ] 是否等于 0 i\ mod\ primes[j]是否等于0 i mod primes[j]是否等于0
第一种情况: i m o d p r i m e s [ j ] = = 0 第一种情况:i\ mod\ primes[j]==0 第一种情况:i mod primes[j]==0
这个判断说明, p r i m e s [ j ] primes[j] primes[j]是 i i i的一个质因数。
也就是说, Φ ( i ) \Phi(i) Φ(i)中存在一项 ( 1 − 1 p r i m e s [ j ] ) (1-\frac{1}{primes[j]}) (1−primes[j]1)
而我们假设 k = i ∗ p r i m e s [ j ] k=i*primes[j] k=i∗primes[j],那么 k k k的算数基本定理的表达式当中只是比 i i i的表达式中, p r i m e s [ j ] primes[j] primes[j]的这一项所对的指数加了1而已。
也就是说,
i ∗ p r i m e s [ j ] i*primes[j] i∗primes[j]和 i i i所含的质因数种类是相同的。
所以二者的欧拉函数中,后面的多项式乘积都是一样的,只是前面的系数 n n n不同,因此我们可以得到下面的结论。
Φ ( i ∗ p r i m e s [ j ] ) = p r i m e s [ j ] ∗ Φ ( i ) \Phi(i*primes[j])=primes[j]*\Phi(i) Φ(i∗primes[j])=primes[j]∗Φ(i)
第二种情况: i m o d p r i m e s [ j ] ! = 0 第二种情况:i\ mod\ primes[j]!=0 第二种情况:i mod primes[j]!=0
因为, p r i m e s [ j ] primes[j] primes[j]不是 i i i的质因数,但是 p r i m e s [ j ] primes[j] primes[j]却是 p r i m e s [ j ] ∗ i primes[j]*i primes[j]∗i的质因数。
此时就说明, p r i m e s [ j ] ∗ i primes[j]*i primes[j]∗i的算术基本定理的表达式中比 i i i的算数基本定理中多了一项 p r i m e s [ j ] primes[j] primes[j]
所以,此时 Φ ( i ∗ p r i m e s [ j ] ) \Phi(i*primes[j]) Φ(i∗primes[j])比 Φ ( i ) \Phi(i) Φ(i)不仅系数多了一个 p r i m e s [ j ] primes[j] primes[j],同时,由于前者的质因数多了一个,所以还多了一项 ( 1 − 1 p r i m e s [ j ] ) (1-\frac{1}{primes[j]}) (1−primes[j]1)
所以:
Φ ( i ∗ p r i m e s [ j ] ) = p r i m e s [ j ] ∗ Φ ( i ) ∗ ( 1 − 1 p r i m e s [ j ] ) = ( p r i m e s [ j ] − 1 ) ∗ Φ ( i ) \Phi(i*primes[j])=primes[j]*\Phi(i)*(1-\frac{1}{primes[j]})=(primes[j]-1)*\Phi(i) Φ(i∗primes[j])=primes[j]∗Φ(i)∗(1−primes[j]1)=(primes[j]−1)∗Φ(i)
所以根据上面三种情况,我们可以将其融合到我们的欧拉筛中。
#include
using namespace std;
const int N=1e6+10;
int primes[N],cnt;
int euler[N];
bool st[N];
void get_eulers(int n)
{
euler[1]=1;
for(int i=2;i<=n;i++)
{
if(!st[i])
{
primes[cnt++]=i;
euler[i]=i-1;
}
for(int j=0;primes[j]<=n/i;j++)
{
st[i*primes[j]]=true;
if(i%primes[j]==0)
{
euler[i*primes[j]]=primes[j]*euler[i];
break;
}
euler[i*primes[j]]=(primes[j]-1)*euler[i];
}
}
}
int main()
{
int n;
cin>>n;
get_eulers(n);
long long res=0;
for(int i=1;i<=n;i++)
{
res+=euler[i];
}
cout<<res<<endl;
return 0;
}