欧拉函数

欧拉函数 \(\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:不想写注释了

例题讲解

洛谷 P2568

题意:给定正整数 \(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\),