一、简述
本文章主要介绍欧拉函数以及快速幂的相关算法。
二、欧拉函数
定义
1 ∼ N 1∼N 1∼N 中与 N N N 互质的数的个数被称为欧拉函数,记为 ϕ ( N ) \phi(N) ϕ(N)。若在算数基本定理中, N = p 1 a 1 p 2 a 2 … p m a m N=p^{a1}_1p^{a2}_2…p^{am}_m N=p1a1p2a2…pmam,则: ϕ ( N ) = N × p 1 − 1 p 1 × p 2 − 1 p 2 × … × p m − 1 p m \phi(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×p1p1−1×p2p2−1×…×pmpm−1。
计算式的证明
情况1
当
n
=
0
n=0
n=0 时,显然,因为
n
=
0
n=0
n=0 时,欧拉函数的计算范围内只有
0
0
0,但是
ϕ
(
0
)
=
0
\phi(0)=0
ϕ(0)=0。
当
n
=
1
n=1
n=1 时,因为
1
1
1 与自身互素,
ϕ
(
1
)
=
1
\phi(1)=1
ϕ(1)=1。
情况2
当 n n n 为素数时,因为 1 ∼ n 1∼n 1∼n 都不被 n n n 整除,所以这些数都与 n n n 互素, ϕ ( n ) = n − 1 \phi(n)=n-1 ϕ(n)=n−1。
情况3
对
n
n
n 进行质因数分解
n
=
p
1
a
1
p
2
a
2
…
p
m
a
m
n=p^{a1}_1p^{a2}_2…p^{am}_m
n=p1a1p2a2…pmam。
将
1
∼
n
1∼n
1∼n 中所有
p
1
,
p
2
.
.
.
p
m
p_1,p_2...p_m
p1,p2...pm 的倍数去掉,剩余
n
−
(
n
∑
i
=
1
m
1
p
i
)
n-(n\sum\limits_{i=1}^m\frac{1}{p_i})
n−(ni=1∑mpi1) 个数,由于该过程
1
∼
n
1∼n
1∼n 中既是
p
i
p_i
pi 又是
p
j
(
i
≠
j
)
p_j(i\neq j)
pj(i=j) 倍数的数被重复去除,所以我们需要将这些数加回来,即
n
−
(
n
∑
i
=
1
m
1
p
i
)
+
(
n
∑
j
,
k
=
1
,
j
≠
k
m
1
p
j
p
k
)
n-(n\sum\limits_{i=1}^m\frac{1}{p_i})+(n\sum\limits_{j,k=1,j\neq k}^m\frac{1}{p_jp_k})
n−(ni=1∑mpi1)+(nj,k=1,j=k∑mpjpk1),以此类推,我们会发现这是多集合容斥计算式,总结为
ϕ
(
N
)
=
N
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
\phi(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×p1p1−1×p2p2−1×…×pmpm−1。
模板题AcWing873.欧拉函数
题目描述
给定 n n n 个正整数 a i a_i ai,请你求出每个数的欧拉函数。
输入格式
第一行包含整数
n
n
n。
接下来
n
n
n 行,每行包含一个正整数
a
i
a_i
ai。
输出格式
输出共 n n n 行,每行输出一个正整数 a i a_i ai 的欧拉函数。
数据范围
1
≤
n
≤
100
,
1≤n≤100,
1≤n≤100,
1
≤
a
i
≤
2
×
1
0
9
1≤a_i≤2×10^9
1≤ai≤2×109
输入样例
3
3
6
8
输出样例
2
2
4
解题思路
根据欧拉函数的计算式计算即可,时间复杂度 O ( n a ) O(n\sqrt{a}) O(na)。
C++代码
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
while(n --)
{
int a;
cin >> a;
int res = a;
for(int i = 2; i <= a / i; i ++)
{
if(a % i == 0)
{
res = res / i * (i - 1);
while(a % i == 0)
a /= i;
}
}
if(a > 1) res = res / a * (a - 1);//先除后乘可以避免数值溢出
cout << res << endl;
}
return 0;
}
三、筛法求欧拉函数
思路
借用线性筛进行欧拉函数的计算
模板题AcWing874.筛法求欧拉函数
题目描述
给定一个正整数 n n n,求 1 ∼ n 1∼n 1∼n 中每个数的欧拉函数之和。
输入格式
共一行,包含一个整数 n n n。
输出格式
共一行,包含一个整数,表示 1 ∼ n 1∼n 1∼n 中每个数的欧拉函数之和。
数据范围
1 ≤ n ≤ 1 0 6 1≤n≤10^6 1≤n≤106
输入样例
6
输出样例
12
解题思路
由于数据范围为
1
0
6
10^6
106,显然我们不能根据定义去依次计算再求解。我们考虑使用线性筛法的方式区计算,具体思路借助代码解释。
关于代码中
f
l
a
g
1
flag1
flag1:根据欧拉函数计算式可知
i
=
p
1
a
1
p
2
a
2
…
p
m
a
m
i=p^{a1}_1p^{a2}_2…p^{am}_m
i=p1a1p2a2…pmam,
ϕ
(
i
)
=
i
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
\phi(i)=i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}
ϕ(i)=i×p1p1−1×p2p2−1×…×pmpm−1;而
t
=
p
r
i
m
e
s
[
j
]
×
i
t=primes[j]\times i
t=primes[j]×i,而
p
r
i
m
e
s
[
j
]
primes[j]
primes[j] 整除
i
i
i,故
t
=
p
1
a
1
p
2
a
2
…
p
m
a
m
t=p^{a1}_1p^{a2}_2…p^{am}_m
t=p1a1p2a2…pmam,除了
t
t
t 的
p
r
i
m
e
s
[
j
]
primes[j]
primes[j] 的指数比
i
i
i 多一,则
ϕ
(
t
)
=
t
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
=
p
r
i
m
e
s
[
j
]
×
i
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
=
p
r
i
m
e
s
[
j
]
×
ϕ
(
i
)
\phi(t)=t\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}=primes[j]\times i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}=primes[j]\times\phi(i)
ϕ(t)=t×p1p1−1×p2p2−1×…×pmpm−1=primes[j]×i×p1p1−1×p2p2−1×…×pmpm−1=primes[j]×ϕ(i)。
关于代码中
f
l
a
g
2
flag2
flag2:
i
i
i 与
p
r
i
m
e
s
[
j
]
primes[j]
primes[j] 互质。则
t
t
t 的质因数分解会比
i
i
i 多一个质因数。
ϕ
(
i
)
=
i
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
\phi(i)=i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}
ϕ(i)=i×p1p1−1×p2p2−1×…×pmpm−1;
ϕ
(
t
)
=
t
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
×
p
r
i
m
e
s
[
j
]
−
1
p
r
i
m
e
s
[
j
]
=
p
r
i
m
e
s
[
j
]
×
i
×
p
1
−
1
p
1
×
p
2
−
1
p
2
×
…
×
p
m
−
1
p
m
×
p
r
i
m
e
s
[
j
]
−
1
p
r
i
m
e
s
[
j
]
=
ϕ
(
i
)
×
(
p
r
i
m
e
s
[
j
]
−
1
)
\phi(t)=t\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\times\frac{primes[j]-1}{primes[j]}=primes[j]\times i\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\times…\times\frac{p_m−1}{p_m}\times\frac{primes[j]-1}{primes[j]}=\phi(i)\times(primes[j]-1)
ϕ(t)=t×p1p1−1×p2p2−1×…×pmpm−1×primes[j]primes[j]−1=primes[j]×i×p1p1−1×p2p2−1×…×pmpm−1×primes[j]primes[j]−1=ϕ(i)×(primes[j]−1)。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long LL;
int n;
int primes[N], cnt;
int euler[N];
bool st[N];
void get_eulers(int n)
{
euler[1] = 1;//1对应的欧拉函数为1
for(int i = 2; i <= n; i ++)
{
if(!st[i])//i为质数
{
primes[cnt ++] = i;//筛出质数
euler[i] = i - 1;//质数对应的欧拉函数值为本身减1
}
for(int j = 0; primes[j] <= n / i; j ++)
{
int t = primes[j] * i;
st[t] = true;
if(i % primes[j] == 0)//primes[j]为i的一个质因数,同时也是t的质因数
{
//flag1
euler[t] = euler[i] * primes[j];
break;
}
//flag2
euler[t] = euler[i] * (primes[j] - 1);
}
}
}
int main()
{
cin >> n;
get_eulers(n);
LL res = 0;
for (int i = 1; i <= n; i ++ ) res += euler[i];
cout << res << endl;
return 0;
}
四、快速幂
正常计算一个数 a a a 的 k k k 次方,则时间复杂度为 O ( k ) O(k) O(k)。而我们换种思路,计算 a a a 的 k k k 次方,我们可以使用 a a a 的 k 2 \frac{k}{2} 2k 自乘计算。那我们就有一种思路 k k k 是偶数 a k = a k 2 × a k 2 a^k=a^\frac{k}{2}\times a^\frac{k}{2} ak=a2k×a2k, k k k 是奇数 a k = a k − 1 × a a^k=a^{k-1}\times a ak=ak−1×a,特别的 k = 0 k=0 k=0, a k = 1 a^k=1 ak=1。这是一种递归的快速幂。那么我们考虑一下将 k k k 拆解为二进制形式,从低位向高位进行计算,每次移位 a a a 都要自乘,且如果当前为 1 1 1,就要与当前的 a a a 相乘。
模板题AcWing875.快速幂
题目描述
给定 n n n 组 a i , b i , p i a_i,b_i,p_i ai,bi,pi,对于每组数据,求出 a i b i m o d p i a^{b_i}_i mod p_i aibimodpi 的值。
输入格式
第一行包含整数
n
n
n。
接下来
n
n
n 行,每行包含三个整数
a
i
,
b
i
,
p
i
a_i,b_i,p_i
ai,bi,pi。
输出格式
对于每组数据,输出一个结果,表示
a
i
b
i
m
o
d
p
i
a^{b_i}_i mod p_i
aibimodpi 的值。
每个结果占一行。
数据范围
1
≤
n
≤
100000
,
1≤n≤100000,
1≤n≤100000,
1
≤
a
i
,
b
i
,
p
i
≤
2
×
1
0
9
1≤a_i,b_i,p_i≤2×10^9
1≤ai,bi,pi≤2×109
输入样例
2
3 2 5
4 3 9
输出样例
4
1
C++代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1;
while(b)
{
if(b & 1) res = res * a % p;
a = (LL) a * a % p;
b >>= 1;
}
return res;
}
int n;
int main()
{
cin >> n;
while(n --)
{
int a, b, p;
cin >> a >> b >> p;
cout << qmi(a, b, p) << endl;
}
return 0;
}
四、乘法逆元
若整数
b
,
m
b,m
b,m 互质,并且对于任意的整数
a
a
a,如果满足
b
∣
a
b|a
b∣a,则存在一个整数
x
x
x,使得
a
b
≡
a
×
x
(
m
o
d
m
)
\frac{a}{b}\equiv a\times x(mod\,m)
ba≡a×x(modm),则称
x
x
x 为
b
b
b 的模
m
m
m 乘法逆元,记为
b
−
1
(
m
o
d
m
)
b^{−1}(mod\,m)
b−1(modm)。
b
b
b 存在乘法逆元的充要条件是
b
b
b 与模数
m
m
m 互质。当模数
m
m
m 为质数时,
b
m
−
2
b^{m−2}
bm−2 即为
b
b
b 的乘法逆元(该求法由下面的欧拉定理给出)。
欧拉定理
若 a a a 与 n n n 互质,则 a ϕ ( n ) ≡ 1 ( m o d n ) a^{\phi(n)}\equiv 1(mod\,n) aϕ(n)≡1(modn)。特别地, n n n 为质数时 ϕ ( n ) = n − 1 \phi(n)=n-1 ϕ(n)=n−1,故 a n − 1 ≡ 1 ( m o d n ) a^{n-1}\equiv 1(mod\,n) an−1≡1(modn)。
模板题AcWing876.快速幂求逆元
题目描述
给定
n
n
n 组
a
i
,
p
i
a_i,p_i
ai,pi,其中
p
i
p_i
pi 是质数,求
a
i
a_i
ai 模
p
i
p_i
pi 的乘法逆元,若逆元不存在则输出 impossible
。
注意:请返回在
0
∼
p
−
1
0∼p−1
0∼p−1 之间的逆元。
输入格式
第一行包含整数
n
n
n。
接下来
n
n
n 行,每行包含一个数组
a
i
,
p
i
a_i,p_i
ai,pi,数据保证
p
i
p_i
pi 是质数。
输出格式
输出共
n
n
n 行,每组数据输出一个结果,每个结果占一行。
若
a
i
a_i
ai 模
p
i
p_i
pi 的乘法逆元存在,则输出一个整数,表示逆元,否则输出 impossible
。
数据范围
1
≤
n
≤
1
0
5
,
1≤n≤10^5,
1≤n≤105,
1
≤
a
i
,
p
i
≤
2
×
1
0
9
1≤a_i,p_i≤2\times 10^9
1≤ai,pi≤2×109
输入样例
3
4 3
8 5
6 3
输出样例
1
2
impossible
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long LL;
LL qmi(int a, int k, int p)
{
LL res = 1;
while(k)
{
if(k & 1) res = res * a % p;
a = (LL) a * a % p;
k >>= 1;
}
return res;
}
int n;
int main()
{
cin >> n;
while(n --)
{
int a, p;
cin >> a >> p;
if(a % p == 0) puts("impossible");
else printf("%lld\n", qmi(a, p - 2, p));
}
return 0;
}