筛法如何高效求欧拉函数?
摘要:63.Acwing基础课第874题-简单-筛法求欧拉函数 题目描述 (给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和)。 输入格式 (共一行,包含一个整数 n)。 输出格式 (共一行,包含一个整数,表示 1∼n 中每个数的
63.Acwing基础课第874题-简单-筛法求欧拉函数
题目描述
\(给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和\)。
输入格式
\(共一行,包含一个整数 n\)。
输出格式
\(共一行,包含一个整数,表示 1∼n 中每个数的欧拉函数之和。\)。
数据范围
\(1≤n≤10^6\),
输入样例:
6
输出样例:
12
代码:
// 包含输入输出流头文件(cin/cout依赖)
#include <iostream>
// 使用std命名空间,避免重复写std::
using namespace std;
// 定义长整型别名:防止求和时溢出(n≤1e6时,欧拉函数和会超出int范围)
typedef long long LL;
// 常量定义:N=1e6+10,适配题目中n≤1e6的场景
const int N = 1000010;
// 全局数组/变量(默认初始化为0/false,无需手动清零)
int primes[N]; // primes数组:存储筛选出的所有质数
int cnt; // cnt:记录质数的个数
int euler[N]; // euler数组:euler[i]表示i的欧拉函数值φ(i)
bool st[N]; // st数组:标记是否为合数(st[i]=true → i是合数)
// 线性筛(欧拉筛)同时求1~n中所有数的欧拉函数值
// 核心:在筛质数的过程中,利用欧拉函数的性质递推计算φ(i)
void get_eulers(int n)
{
// 欧拉函数性质1:φ(1)=1(1与自身互质,1~1中只有1个数)
euler[1] = 1;
// 遍历2~n的所有数,筛质数+递推欧拉函数
for (int i = 2; i <= n; i ++ )
{
// 如果i未被标记(是质数)
if (!st[i])
{
primes[cnt ++ ] = i; // 存入质数数组
// 欧拉函数性质2:若p是质数,则φ(p)=p-1(1~p中除了p都与p互质)
euler[i] = i - 1;
}
// 遍历已找到的质数,标记合数+递推欧拉函数
for (int j = 0; primes[j] <= n / i; j ++ )
{
int t = primes[j] * i; // 当前要标记的合数t = 质数primes[j] × i
st[t] = true; // 标记t为合数
// 关键:根据i与primes[j]的整除关系,分两种情况递推φ(t)
if (i % primes[j] == 0)
{
// 情况1:primes[j]是i的质因子(i能被primes[j]整除)
// 欧拉函数性质3:若p是质数,且p|i,则φ(i×p) = φ(i) × p
euler[t] = euler[i] * primes[j];
break; // 线性筛核心:保证每个合数只被最小质因子筛一次
}
// 情况2:primes[j]不是i的质因子(i不能被primes[j]整除)
// 欧拉函数性质4:若p是质数,且p不整除i,则φ(i×p) = φ(i) × (p-1)
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
int main()
{
int n; // n:输入的上限,要求计算1~n的欧拉函数之和
cin >> n;
// 调用函数,筛出1~n的质数并计算所有数的欧拉函数值
get_eulers(n);
LL res = 0; // 存储1~n欧拉函数的和,用LL防止溢出
// 累加1~n所有数的欧拉函数值
for (int i = 1; i <= n; i ++ )
res += euler[i];
// 输出最终的和
cout << res << endl;
return 0; // 程序正常结束
}
