欧拉函数 \(\varphi\) 的笔记
前置知识:
欧拉函数的定义
欧拉函数是OI竞赛中十分重要的积性函数,欧拉函数 \(\varphi(n)\) 表示 \(1\sim n\) 中与 \(n\) 互质的数的个数,特别的,\(\varphi(1) = 1\)。
ps:懒得证明欧拉函数的积性性质了
若 \(n \perp m\),则 \(\varphi(nm)= \varphi(n) \times \varphi(m)\)。
在证明了欧拉函数是一个积性函数后,就只需要考虑 \(\varphi(p^k)\) 的取值就可以计算出所有数的欧拉函数了。如果一个数若与 \(p^k\) 不互质,那么这个数一定是 \(p\) 的倍数,所以 \(1\sim p^k\) 中一共有 \(p^{k-1}\) 个数和 \(p^k\) 不互质,反过来 \(\varphi(p^k)=p^k-p^{k-1}=p^k\times (1- \frac{1}{p})\)。所以设 \(n=p_1^{k_1}\times p_2^{k_2}\times ...\times p_m^{k_m}\),则 \(\varphi(n)=n\times(1-\frac{1}{p_1})\times (1-\frac{1}{p_2})\times ...\times (1-\frac{1}{p_m})\)。
求解欧拉函数
\(O(\sqrt(n))\) 的时间复杂度求解 \(\varphi(n)\)
cpp
phi = n;//phi 表示 phi(n)
for(int i = 2;i * i <= n;i ++)
if(n % i == 0)
{
phi = phi / i * (i - 1);//乘上 (1 - 1 / p)
while(n % i == 0) n /= i;//把 n 中的 i 全部筛去
}
if(n != 1) phi = phi / n * (n - 1);
线性筛求解 \(\varphi(1)\sim \varphi(n)\)
我们根据上面的代码可以反洗出线性筛求解欧拉函数的代码:
cpp
phi[i] = 1;
for(int i = 2;i <= n;i ++)
{
if(phi[i] == 0)
p[++ tot] = i, phi[i] = i - 1;
for(int j = 1;j <= tot && i * p[j] <= n;j ++)
{
if(i % p[j] == 0)
{
phi[i * p[j]] = phi[i] * p[j];
break;
}
else phi[i * p[j]] = phi[i] * (p[j] - 1);
}
}
ps:不想写注释了
例题讲解
题意:给定正整数 \(n\),求 \(1\le x,y\le n\) 并且 \(\gcd(x,y)\) 为质数的数对 \((x,y)\) 有多少对,\(n\le 10^7\)。
思路:
众所周知,线性筛可以快速求出 \(\le n\) 的所有质数,那么就可以将问题转化为对于每一个 \(i\) 求 \(\gcd(x,y)=i\) 的个数。
设 \(x=x'i,y=y'i\),