欧拉函数
题目
给定 \(n\) 个正整数 \(a_i\),请你求出每个数的欧拉函数。
欧拉函数的定义
\(1 \sim N\) 中与 \(N\) 互质的数的个数被称为欧拉函数,记为 \(ϕ(N)\)。
若在算数基本定理中,\(N = p_1^{a_1}p_2^{a_2}…p_m^{a_m}\),则:
\(ϕ(N)\) = \(N \times \frac{p_1-1}{p_1} \times \frac{p_2-1}{p_2} \times … \times \frac{p_m-1}{p_m}\)
输入格式
第一行包含整数 \(n\)。
接下来 \(n\) 行,每行包含一个正整数 \(a_i\)。
输出格式
输出共 \(n\) 行,每行输出一个正整数 \(a_i\) 的欧拉函数。
数据范围
\(1 \le n \le 100\),
\(1 \le a_i \le 2 \times 10^9\)
输入样例:
3
3
6
8
输出样例:
2
2
4
题解
欧拉函数的公式推导
任何一个正整数\(N\)都可以用质数幂的乘积表示出来 \(N = p_1^{a_1}p_2^{a_2}…p_m^{a_m}\)
欧拉函数用来求 在小于或等于\(N\)的正整数中 与\(N\)互质的数的数目
利用容斥原理
推导该公式
\(ϕ(N)\) = \(N \times \frac{p_1-1}{p_1} \times \frac{p_2-1}{p_2} \times … \times \frac{p_m-1}{p_m}\)
代码实现
#include <bits/stdc++.h>
using namespace std;
#define int long long
int T;
int n;
void solve()
{
cin >> n;
//分解质因数
int res = n; //欧拉公式里先要乘一个n
for (int i = 2; i <= n / i; i ++ )
{
if (n % i == 0) //找到质因数
{
res = res / i * (i - 1); //套公式 乘上公式里找到的质因数部分
while (n % i == 0) n /= i; //把该质因数i的倍数从n里除干净
}
}
if (n > 1) //如果还含有一个大于sqrt(n)的质因子(最多一个)
res = res / n * (n - 1);
//res *= (n - 1) / n;不能这么写,(n - 1) / n 一定是个小数,一旦出现小数由于int向下取整的特性小数就变成0了
cout << res << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> T;
while (T -- )
{
solve();
}
return 0;
}
一个细节
for (int i = 2; i <= n / i; i ++ )
{
if (n % i == 0) //找到质因数
{
res *= (i - 1) / i; //套公式 乘上公式里找到的质因数部分
while (n % i == 0) n /= i; //把该质因数i的倍数从n里除干净
}
}
if (n > 1) //如果还含有一个大于sqrt(n)的质因子(最多一个)
res *= (n - 1) / n;
这里的res *= (i - 1) / i;
是完全依照公式\(\frac{p_1-1}{p_1}\)的写法,但这样写有一个非常致命的问题,由于int
是向0取整的,(i - 1) / i
必然是一个小数,那在代码里实际的值是0
,就会导致我们的运算结果出错,这里就必须要换一种写法,我们先对res / i
就不会出现小数,由于i是质因数res / i
得到的必然是一个整数,所以就规避了小数的问题,这种写法与原式等价res = res / i * (i - 1);