1 基础知识
已知数i、质数a和数i的欧拉函数值 ϕ ( i ) \phi(i) ϕ(i),求数i*a的欧拉函数值 ϕ ( i ⋅ a ) \phi(i\cdot a) ϕ(i⋅a)。
- 如果质数a是数i的质因子(即a % i == 0),那么有
ϕ
(
a
⋅
i
)
=
a
⋅
ϕ
(
i
)
\phi(a\cdot i)=a\cdot \phi(i)
ϕ(a⋅i)=a⋅ϕ(i)。证明如下,
ϕ ( a ⋅ i ) = a ⋅ i ⋅ Σ j ( 1 − 1 p j ) = a ⋅ ϕ ( i ) \phi(a\cdot i)=a\cdot i \cdot \Sigma_{j}(1-\frac{1}{p_j})=a\cdot \phi(i) ϕ(a⋅i)=a⋅i⋅Σj(1−pj1)=a⋅ϕ(i)
注意 p j p_j pj中包含了a。 - 如果质数a不是数i的质因子(即a % i != 0),那么有
ϕ
(
a
⋅
i
)
=
(
a
−
1
)
⋅
ϕ
(
i
)
\phi(a\cdot i)=(a - 1) \cdot \phi(i)
ϕ(a⋅i)=(a−1)⋅ϕ(i)。证明如下,
ϕ ( a ⋅ i ) = a ⋅ i ⋅ Σ j ( 1 − 1 p j ) ⋅ ( 1 − 1 a ) = a ⋅ ϕ ( i ) ⋅ ( 1 − 1 a ) = ( a − 1 ) ⋅ ϕ ( i ) \phi(a\cdot i)=a \cdot i \cdot \Sigma_j(1-\frac{1}{p_j}) \cdot (1-\frac{1}{a})=a\cdot \phi(i) \cdot (1-\frac{1}{a})=(a-1)\cdot \phi(i) ϕ(a⋅i)=a⋅i⋅Σj(1−pj1)⋅(1−a1)=a⋅ϕ(i)⋅(1−a1)=(a−1)⋅ϕ(i)
注意 p j p_j pj中并没有包含a(因为a不是i的质因子)。
质数的欧拉函数值等于它本身减1。
故可以在线性筛法求1~n的质数中,计算每个数的欧拉函数值,代码如下,
int st[N];
int primes[N];
int phi[N];
int cnt;
void get_phis(int n) {
phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) {
primes[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; ++j) {
st[i * primes[j]] = true;
if (i % primes[j] == 0) {
phi[i * primes[j]] = primes[j] * phi[i];
break;
}
phi[i * primes[j]] = (primes[j] - 1) * phi[i];
}
}
//phi[i]即为i的欧拉函数值
}
欧拉定理:若a与b互质(即a和b的最大公约数为1),那么有 a ϕ ( b ) % b = = 1 a^{\phi(b)} \% b == 1 aϕ(b)%b==1。
费马定理:若a与b互质且b是质数,那么有 a b − 1 % b = = 1 a^{b-1}\% b==1 ab−1%b==1。
2 模板
int primes[N], cnt; // primes[]存储所有素数
int euler[N]; // 存储每个数的欧拉函数
bool st[N]; // st[x]存储x是否被筛掉
void get_eulers(int n)
{
euler[1] = 1;
for (int i = 2; i <= n; i ++ )
{
if (!st[i])
{
primes[cnt ++ ] = i;
euler[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; j ++ )
{
int t = primes[j] * i;
st[t] = true;
if (i % primes[j] == 0)
{
euler[t] = euler[i] * primes[j];
break;
}
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
3 工程化
题目1:计算1~n的欧拉函数值的和。
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n, cnt;
int primes[N];
bool st[N];
int phi[N];
void get_phis() {
phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) {
primes[cnt++] = i;
phi[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; ++j) {
st[i * primes[j]] = true;
if (i % primes[j] == 0) {
phi[i * primes[j]] = primes[j] * phi[i];
break;
}
phi[i * primes[j]] = (primes[j] - 1) * phi[i];
}
}
long long res = 0;
for (int i = 1; i <= n; ++i) {
res += phi[i];
}
cout << res << endl;
return;
}
int main() {
cin >> n;
get_phis();
return 0;
}