欧拉函数

欧拉函数 \(\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)\)

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)\)

我们根据上面的代码可以反洗出线性筛求解欧拉函数的代码:

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\),那么 \(x'\perp y'\) 并且 \(1\le x',y'\le \frac{n}{i}\)。继续转化为对于每个 \(i\),求 \(x'\perp y'\)\(1\le x',y'\le \frac{n}{i}\) 的数对 \((x',y')\) 的个数。先考虑 \(x'\le y'\) 当情况,如果 \(y'=k\),则方案数为 \(\varphi(k)\),所以 \(x'\le y'\) 的总方案数为 \(\varphi(1)+\varphi(2)+...+\varphi(\frac{n}{i})\)。而 \(x'\ge y'\) 的情况也一样,不过要注意 \(x'=y'=1\) 的情况被计算了两次,最后的方案数要减一。

只需要线性筛时求出欧拉函数,再预处理前缀和即可。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e7 + 10;
int n, tot, ans;
int phi[N], sumphi[N], p[N];
signed main()
{
	cin >> n;
	phi[1] = sumphi[1] = 1;
	for(int i = 2;i <= n;i ++)
	{
		if(phi[i] == 0)
			p[++ tot] = i, phi[i] = i - 1;
		sumphi[i] = sumphi[i - 1] + phi[i];
		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);
		}
	}//线性筛求解欧拉函数
	for(int i = 1;i <= tot;i ++)
		ans += 2 * sumphi[n / p[i]] - 1;//计算方案数
	cout << ans;
	return 0;
}
posted @ 2025-06-16 20:22  Loyal_Soldier  阅读(32)  评论(0)    收藏  举报