『欧拉函数与线性筛』
<更新提示>
<第一次更新>
<正文>
欧拉函数
定义
\(\forall\ a,b\in N\),若\(gcd(a,b)=1\),则称\(a\),\(b\)互质。
对于一个正整数\(n\),我们将\(1-n\)中与\(n\)互质的数的个数称为欧拉函数,记为\(\phi(n)\)。
求解
若在算数基本定理中,有\(n=p_1^{a_1}*p_2^{a_2}*...*p_k^{a_k}\),则可以得到:
证明:
设\(p\)为\(n\)的一个质因子,则\(1-n\)中\(p\)的倍数有\(p,2p,3p,...,\frac{n}{p}p\),即\(1-n\)中不含有因子\(p\)的数的个数为\(n-\frac{n}{p}\)个。
设\(q\)为\(n\)的另一个质因子,由容斥原理可得\(1-n\)中不含有因子\(p\),\(q\)的数的个数为\(n -\frac{n}{p}- \frac{n}{q}+ \frac{n}{pq}=n(\frac{p-1}{p})(\frac{q-1}{q})\)个。
推广到\(n\)的每一个质因子,可得\(1-n\)中不与\(n\)含有任何共同质因子的数的个数为\(n*\prod_{i=1}^{k}\frac{p_i-1}{p_i}\)个,即\(\phi(n)=n*\prod_{i=1}^{k}\frac{p_i-1}{p_i}\)。
利用如上公式,我们可以直接在分解质因数的过程中求得一个数的欧拉函数,时间复杂度为\(O(\sqrt n)\)。
\(Code:\)
inline long long phi(long long x)
{
long long ans=x;
for (long long i=2;i*i<=x;i++)
{
if (x%i==0)
{
ans=ans/i*(i-1);
while (x%i==0)x/=i;
}
}
if (x>1)ans=ans/x*(x-1);
return ans;
}
性质
\(1.\)对于质数\(p\),有\(\phi(p)=p-1\)
证明:由定义可得。
\(2.\)\(\forall \ n>1\),\(1-n\)中与\(n\)互质的数的和为\(\frac{n}{2}\phi (n)\)
证明:
由于\(gcd(n,x)=gcd(n,n-x)\),所以与\(n\)不互质的数\(n-x,x\)成对出现,且平均值为\(\frac{n}{2}\),因此与\(n\)互质的数的平均值也为\(\frac{n}{2}\),\(n\)互质的数的和即为\(\frac{n}{2}\phi (n)\)。
\(3.\)若\(gcd(a,b)=1\),则\(\phi(ab)=\phi(a)\phi(b)\)
\(4.\)欧拉函数是积性函数,即\(n=\prod p_i^{a_i}\),\(\phi(n)=\prod \phi(p_i^{a_i})\)
\(5.\)对于一个质数\(p\),若有\(p|n,p^2|n\),则\(\phi(n)=\phi(\frac{n}{p})*p\)
\(6.\)对于一个质数\(p\),若有\(p|n,p^2\not | n\),则\(\phi(n)=\phi(\frac{n}{p})*(p-1)\)
证明:
可以由欧拉函数的计算式直接推导得到。
\(7.\)\(\sum_{d|n}\phi(d)=n\)
证明:
设函数\(f(x)=\sum_{d|n}\phi(d)\),则\(f(nm)=\sum_{d|nm}\phi(d)\),若\(n,m\)互质,利用欧拉函数是积性函数可以得到:$$f(nm)=\sum_{d|nm}\phi(d)=\sum_{d_1|n}\sum_{d_2|m}\phi(d_1d_2)=\sum_{d|n}\phi(d)*\sum_{d|m}\phi(d)=f(n)f(m)$$
即函数\(f\)为积性函数。
对于一个质数\(p\),$$f(pm)=\sum_{d|pm}\phi(d)=\sum{m}_{i=0}\phi(pi)$$,由等差数列求和可得\(f(p^m)=p^m\),即可得到$$f(n)=\prod_{i=1}{m}f(p_i)=\prod_{i=1}{m}p_i=n$$
这些性质在不同的题目中有不同的作用,需要我们注意灵活运用。特别是性质\(6.\),在\(dirichlet\)卷积,\(Möbius\)反演中等内容中也会起到作用。
线性筛求解
对于求解\(1-n\)所有数的欧拉函数,如果直接使用分解质因数法,时间复杂度为\(O(n\sqrt n)\)。
事实上,我们可以利用欧拉函数的性质\(1.\ 5.\ 6.\),再线性筛的求解过程中顺带地求解欧拉函数(线性筛见『素数 Prime判定和线性欧拉筛法 The sieve of Euler』)。
\(Code:\)
inline void eular(void)
{
for (int i=2;i<=n;i++)
{
if (!flag[i])Prime[++cnt]=i,phi[i]=i-1;
for (int j=1;j<=cnt&&i*Prime[j]<=n;j++)
{
flag[ i*Prime[j] ] = true;
if (i%Prime[j]==0)
{
phi[ i*Prime[j] ] = phi[i] * Prime[j];
break;
}
else phi[ i*Prime[j] ] = phi[i] * (Prime[j]-1);
}
}
}
<后记>
感谢\(hzk\) 欧拉函数的介绍(附加欧拉定理和费马小定理的介绍)。