欧拉函数
【互质】
• a 与 b 互质:说明 a 与 b 的最⼤公约数是 1 。
【欧拉函数】
• 对于⼀个数 n ,在 1 ∼ n 中,与 n 互质的数的个数,就是欧拉函数,⽤ φ ( n ) 表⽰。
⽐如: φ (1) = 1 , φ (2) = 1 , φ (3) = 2 , φ (4) = 2 , φ (5) = 4 ......
【积性函数】
• 若函数f(n) 满⾜:f(1)=1 ,且f(xy)=f(x)f(y) x,y都属于N* 都成⽴。则 f(n)为积性
函数。
【欧拉函数的性质】
- 若 p 为质数,则 φ ( p ) = p − 1 ;
- 若 p 为质数,则 φ ( p k ) = ( p − 1) p k −1 ;
- 欧拉函数为积性函数: ,其中 。(感兴趣的同学研究⼀下证
明,因为⽐较繁琐,这⾥不做证明。)

cpp
块int phi(int n)
{
int ret = n;
for(int i = 2; i <= n / i; i++)
{
if(n % i == 0)
{
ret = ret / i * (i - 1); // 先除后乘,保证不会溢出
while(n % i == 0) n /= i;
}
}
// 别忘记判断最后⼀个数
if(n > 1) ret = ret / n * (n - 1);
return ret;
}

cpp
#include <iostream>
using namespace std;
const int N = 100010; // 根据需求定义数组大小(需大于输入的n)
int n; // 目标范围上限
bool st[N]; // 标记是否为合数(true=合数,false=质数)
int p[N], cnt; // p[]存储质数,cnt是质数计数
int phi[N]; // phi[x]存储x的欧拉函数值
// 线性筛求1~n的欧拉函数
void get_phi() {
phi[1] = 1; // 欧拉函数定义:φ(1)=1(1与自身互质)
// 枚举2~n的所有数
for (int i = 2; i <= n; i++) {
// 若i是质数(未被标记)
if (!st[i]) {
p[++cnt] = i; // 加入质数数组
phi[i] = i - 1; // 质数的欧拉函数:φ(p)=p-1(1~p-1都与p互质)
}
// 用当前质数p[j]筛去i*p[j]
for (int j = 1; 1LL * i * p[j] <= n; j++) {
int x = i * p[j]; // 要筛的合数x = i * p[j]
st[x] = true; // 标记x为合数
// 情况1:p[j]是i的最小质因数(i能被p[j]整除)
if (i % p[j] == 0) {
// 欧拉函数性质:若p是x的质因数且x = p^k * m(m与p互质),则φ(x)=p*φ(m)
phi[x] = p[j] * phi[i];
break; // 线性筛核心:保证每个数仅被最小质因数筛去,直接break
}
// 情况2:p[j]不是i的质因数(i与p[j]互质)
else {
// 欧拉函数积性:若a与b互质,则φ(a*b)=φ(a)*φ(b)
phi[x] = (p[j] - 1) * phi[i];
}
}
}
}
// 测试示例
int main() {
cin >> n;
get_phi();
// 输出1~n的欧拉函数值(可选)
for (int i = 1; i <= n; i++) {
cout << "φ(" << i << ") = " << phi[i] << endl;
}
return 0;
}
时间复杂度:
与线性筛时间复杂度⼀致,为 O ( n ) 。
1 仪仗队
题⽬来源: 洛⾕
题⽬链接: P2158 [SDOI2008] 仪仗队
难度系数: ★★★★
题目描述
作为体育委员,C 君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 N×N 的方阵,为了保证队伍在行进中整齐划一,C 君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

现在,C 君希望你告诉他队伍整齐时能看到的学生人数。
输入格式
一行,一个正整数 N。
输出格式
输出一行一个数,即 C 君应看到的学生人数。
输入输出样例
输入 #1复制
4
输出 #1复制
9
说明/提示
对于 100% 的数据,1≤N≤40000。
【解法】
对于坐标 ( i , j ) ,当且仅当 i 与 j 互质的时,才能被看到。
因此,统计出 [1, n − 1] 的欧拉函数之和,乘 2 再加 1 就是结果。
【参考代码】
cpp
#include <iostream>
using namespace std;
typedef long long LL; // 防止数值溢出
const int N = 40010; // 适配题目中N的范围(通常N≤40000)
int n; // 输入的方阵边长
bool st[N]; // 标记是否为合数
int p[N], cnt; // p[]存储质数,cnt是质数计数
int phi[N]; // phi[x]存储x的欧拉函数值
// 线性筛(欧拉筛)预处理1~n的欧拉函数
void get_phi() {
phi[1] = 1; // 欧拉函数定义:φ(1)=1
for (int i = 2; i <= n; i++) {
// 若i是质数
if (!st[i]) {
phi[i] = i - 1; // 质数的欧拉函数:φ(p)=p-1
p[++cnt] = i; // 加入质数数组
}
// 用当前质数筛去合数,同时计算欧拉函数
for (int j = 1; 1LL * i * p[j] <= n; j++) {
int x = i * p[j];
st[x] = true; // 标记为合数
// 情况1:p[j]是i的最小质因数
if (i % p[j] == 0) {
phi[x] = phi[i] * p[j];
break; // 线性筛核心:保证每个数仅被最小质因数筛去
}
// 情况2:p[j]与i互质(积性性质)
else {
phi[x] = phi[i] * (p[j] - 1);
}
}
}
}
int main() {
cin >> n;
// 特殊情况:N=1时无学生
if (n == 1) {
cout << 0 << endl;
return 0;
}
// 预处理欧拉函数
get_phi();
// 统计1~n-1的欧拉函数之和
LL sum = 0;
for (int i = 1; i < n; i++) {
sum += phi[i];
}
// 对称乘2 + 正前方1个
cout << sum * 2 + 1 << endl;
return 0;
}
2 GCD
题⽬来源: 洛⾕
题⽬链接: P2568 GCD
难度系数: ★★★★
题目描述
给定正整数 n,求 1≤x,y≤n 且 gcd(x,y) 为素数的数对 (x,y) 有多少对。
输入格式
只有一行一个整数,代表 n。
输出格式
一行一个整数表示答案。
输入输出样例
输入 #1复制
4
输出 #1复制
4
说明/提示
样例输入输出 1 解释
对于样例,满足条件的 (x,y) 为 (2,2),(2,4),(3,3),(4,2)。
数据规模与约定
- 对于 100% 的数据,保证 1≤n≤107。
来源:bzoj2818。
本题数据为洛谷自造数据,使用 CYaRon 耗时 5 分钟完成数据制作。
【解法】
构造 x , y 数对。
设: gcd( x , y ) = d ,其中 x = ad , y = bd ,那么 gcd( x , y ) = gcd( ad , bd ) = d × gcd( a , b ) 。
可得: gcd( a , b ) = 1 ,也就是 a , b 互质。
其中,x,y 的最⼤公约数为d ,且 d为质数,那么 x,y应该在d , 2 d , 3 d , 4 d ... × 中挑选,且
系数互质。
问题就变成求每⼀个数的欧拉函数。
【参考代码】
cpp
#include <iostream>
using namespace std;
typedef long long LL; // 防止数值溢出
const int N = 1e7 + 10; // 适配n的最大值(题目中n≤1e7)
int n; // 输入的正整数n
bool st[N]; // 标记是否为合数
int p[N], cnt; // p[]存储质数,cnt是质数计数
int phi[N]; // phi[x]存储x的欧拉函数值
LL f[N]; // f[k] = φ(1)+φ(2)+...+φ(k)(前缀和)
// 线性筛(欧拉筛)预处理1~n的欧拉函数+质数数组
void get_phi() {
phi[1] = 1; // 欧拉函数定义:φ(1)=1
for (int i = 2; i <= n; i++) {
// 若i是质数
if (!st[i]) {
phi[i] = i - 1; // 质数的欧拉函数:φ(p)=p-1
p[++cnt] = i; // 加入质数数组
}
// 筛合数并计算欧拉函数
for (int j = 1; 1LL * i * p[j] <= n; j++) {
int x = i * p[j];
st[x] = true;
// 情况1:p[j]是i的最小质因数
if (i % p[j] == 0) {
phi[x] = phi[i] * p[j];
break; // 线性筛核心:保证每个数仅被最小质因数筛去
}
// 情况2:p[j]与i互质(积性性质)
else {
phi[x] = phi[i] * (p[j] - 1);
}
}
}
}
int main() {
cin >> n;
// 预处理欧拉函数和质数数组
get_phi();
// 预处理欧拉函数前缀和f[k] = φ(1)+φ(2)+...+φ(k)
for (int i = 1; i <= n; i++) {
f[i] = f[i - 1] + phi[i];
}
LL sum = 0; // 最终答案
// 遍历所有质数d,计算每个d对应的数对数量
for (int i = 1; i <= cnt; i++) {
int d = p[i]; // 当前质数d
int k = n / d; // a,b的上限k = n/d
sum += f[k] * 2 - 1;// 累加当前d对应的数对数量
}
cout << sum << endl;
return 0;
}