0.食用指北
- ( a , b ) (a,b) (a,b) 表示 gcd ( a , b ) \gcd(a,b) gcd(a,b) ,也就是 a a a 与 b b b 的最大公约数;
- [ a , b ] [a,b] [a,b] 表示 lcm ( a , b ) \operatorname{lcm}(a,b) lcm(a,b) ,也就是 a a a 与 b b b 的最小公倍数;
- i n v ( a , b ) inv(a,b) inv(a,b) 表示 a a a 在膜 b b b 意义下的逆元;
-
p
p
p 的简化剩余系是什么?
当且仅当 ( x , p ) = 1 且 x < p (x,p)=1且x<p (x,p)=1且x<p 时, x x x 属于 p p p 的简化剩余系。 - [ x , y ) [x,y) [x,y) 表示左闭右开的区间;
- 重要结论均用红色突出了,可以重点阅读这些部分;
- 学
2.3
前,你需要知道线性筛。
1.欧拉函数基础概念
1.1 欧拉函数的定义
欧拉函数表示一个数的简化剩余系的元素数。
或者说,
对
于
一
个
数
p
,
它
的
欧
拉
函
数
值
即
为
[
1
,
p
)
∩
Z
内
与
p
互
质
的
数
的
个
数
。
\color{red}对于一个数 p ,它的欧拉函数值即为 [1,p)\cap\Z 内与 p 互质的数的个数。
对于一个数p,它的欧拉函数值即为[1,p)∩Z内与p互质的数的个数。
特
殊
地
,
1
的
欧
拉
函
数
值
是
1
。
\color{red}特殊地,1 的欧拉函数值是 1 。
特殊地,1的欧拉函数值是1。
欧
拉
函
数
的
数
学
符
号
写
作
ϕ
,
手
写
体
为
φ
。
\color{red}欧拉函数的数学符号写作 \phi ,手写体为 \varphi 。
欧拉函数的数学符号写作ϕ,手写体为φ。
1.2 欧拉函数的基本性质
- 1.2.1 钦定 φ ( 1 ) = 1 \varphi(1)=1 φ(1)=1.
- 1.2.2 若 p 为 质 数 , 则 φ ( p ) = p − 1. \color{red}若p为质数,则\varphi(p)=p-1. 若p为质数,则φ(p)=p−1.
- 1.2.3 若 p p p 为质数,则 φ ( p k ) = p k − p k − 1 \varphi(p^k)=p^k-p^{k-1} φ(pk)=pk−pk−1.
- 1.2.4 欧 拉 函 数 是 积 性 函 数 , 也 就 是 说 若 ( M , N ) = 1 , 则 φ ( M N ) = φ ( M ) φ ( N ) . \color{red}欧拉函数是积性函数,也就是说若(M,N)=1,则 \varphi(MN)=\varphi(M)\varphi(N). 欧拉函数是积性函数,也就是说若(M,N)=1,则φ(MN)=φ(M)φ(N).
- 1.2.5 欧 拉 函 数 的 计 算 公 式 : φ ( x ) = x × ∏ i = 1 n ( 1 − 1 p i ) , 其 中 p 1 , p 2 , … , p n 为 x 的 所 有 质 因 数 . \color{red}欧拉函数的计算公式: \varphi(x)=x\times\prod\limits_{i=1}^n(1-\dfrac 1{p_i}),其中p_1,p_2,\dots,p_n为x的所有质因数. 欧拉函数的计算公式:φ(x)=x×i=1∏n(1−pi1),其中p1,p2,…,pn为x的所有质因数.
- 1.2.6 若 p p p 为质数, x m o d p = 0 x \bmod p=0 xmodp=0,则 φ ( x p ) = φ ( x ) × p \varphi(xp)=\varphi(x)\times p φ(xp)=φ(x)×p
- 1.2.7 若 p p p 为质数, x m o d p ≠ 0 x \bmod p\ne 0 xmodp=0,则 φ ( x p ) = φ ( x ) φ ( p ) \varphi(xp)=\varphi(x)\varphi(p) φ(xp)=φ(x)φ(p)
- 有兴趣的话可以看看:以上七点的证明
2.欧拉函数的求法
2.1 O ( n ) 求 φ ( n ) \color{red}\mathcal O(\sqrt n) 求 \varphi(n) O(n)求φ(n)
根据 1.2.5 的公式就可以做到啦
int zch_txdy(int n) {
int ans=n,n2=n;
for (int i=2; i*i<=n2; ++i) { //枚举√n2以下的质因子
if (n2%i==0) ans=ans/i*(i-1); //先做除法,防止溢出
while (n2%i==0) n2/=i;
}
if (n2>1) ans=ans/n2*(n2-1); //单独处理√n2以上的质因子
return ans;
}
2.2 O ( n log log n ) \mathcal O(n\log\log n) O(nloglogn) 求 φ ( 1 ) ∼ φ ( n ) \varphi(1)\sim \varphi(n) φ(1)∼φ(n)
依然和 1.2.5 的公式有关。
φ
(
x
)
=
x
×
∏
i
=
1
n
(
1
−
1
p
i
)
\varphi(x)=x\times\prod\limits_{i=1}^n(1-\dfrac 1{p_i})
φ(x)=x×i=1∏n(1−pi1)
其中
x
x
x 这一项无论如何都会乘上的,所以我们可以这样初始化:
for (int i=1; i<=n; ++i) phi[i]=i;
p
i
p_i
pi 是
x
x
x 的质因子,所以我们可以用满足
p
∣
x
p|x
p∣x 的质数
p
p
p 去筛
x
x
x : phi[x]=phi[x]/p*(p-1)
。
int phi[500010];
void zch_ddjxd(int n) {
for (int i=1; i<=n; ++i) phi[i]=i;
for (int i=2; i<=n; ++i)
if (phi[i]==i) //未被其它数筛过,说明它是质数
for (int j=i; j<=n; j+=i) //用i去筛所有i的倍数
phi[j]=phi[j]/i*(i-1);
}
容易看出这份代码和埃氏筛法的时间复杂度相同,也是 O ( n log log n ) \mathcal O(n \log \log n) O(nloglogn)
2.3 O ( n ) 求 φ ( 1 ) ∼ φ ( n ) \color{red}\mathcal O(n)求\varphi(1)\sim\varphi(n) O(n)求φ(1)∼φ(n)
线性筛的过程中加几句话就可以求欧拉函数了。具体看代码吧。
v
v
v 存的是一个附带信息(
i
i
i 的最小质因子),读者可以不管。
const int N=1000010;
int cnt,v[N],p[N],phi[N];
void zch_AKIOI(int n) { //可筛素数、求欧拉函数
for (int i=2; i<=n; ++i) {
if (!v[i]) {
p[++cnt]=i; //筛素数
phi[i]=i-1; //1.2.2
v[i]=i;
}
for (int j=1; j<=cnt; ++j) {
if (1ll*p[j]*i>n) break; //当心爆int
int t=p[j]*i;
v[t]=p[j]; //t的最小质因子是p[j]
if (i%p[j]==0) {
phi[t]=phi[i]*p[j]; //1.2.6
break; //每个数只被它的最小质因子筛一次
}
else phi[t]=phi[i]*phi[p[j]]; //1.2.7
}
}
}
3. 欧拉定理
若 a , p 为 一 对 互 质 的 正 整 数 , 则 a φ ( p ) ≡ 1 ( m o d p ) . \color{red} 若 a,p 为一对互质的正整数,则 a^{\varphi(p)} \equiv 1 \pmod p. 若a,p为一对互质的正整数,则aφ(p)≡1(modp).
证明:
设
p
p
p 的简化剩余系为
X
X
X ,元素记作
x
1
,
x
2
,
…
,
x
φ
(
p
)
x_1,x_2,\dots,x_{\varphi(p)}
x1,x2,…,xφ(p) 。
先来证明
∏
i
=
1
φ
(
p
)
x
i
≡
∏
i
=
1
φ
(
p
)
(
x
i
×
a
)
(
m
o
d
p
)
\prod\limits_{i=1}^{\varphi(p)}x_i\equiv \prod\limits_{i=1}^{\varphi(p)}(x_i\times a)\pmod p
i=1∏φ(p)xi≡i=1∏φ(p)(xi×a)(modp)
这一段的证明与费马小定理中的一段证明极其相似,所以你直接去看这篇文章就行
式子两边约掉与
p
p
p 互质的
∏
i
=
1
φ
(
p
)
x
i
\prod\limits_{i=1}^{\varphi(p)}x_i
i=1∏φ(p)xi 就得到了
a
φ
(
p
)
≡
1
(
m
o
d
p
)
a^{\varphi(p)}\equiv 1 \pmod p
aφ(p)≡1(modp) 。
补充一点,当
p
p
p 取质数时,
φ
(
p
)
=
p
−
1
\varphi(p)=p-1
φ(p)=p−1 ,根据欧拉定理,当
a
,
p
a,p
a,p 互质时,
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}\equiv 1\pmod p
ap−1≡1(modp)
上面这个就是费马小定理。所以费马小定理是欧拉定理的特殊情况,这也是为什么它们的证明如此相似的原因。
4.扩展欧拉定理
扩展欧拉定理可用于
a
,
p
a,p
a,p 不互质的情况。
b
≥
φ
(
p
)
时
,
a
b
≡
a
b
m
o
d
φ
(
p
)
+
φ
(
p
)
(
m
o
d
p
)
.
\color{red} b\ge \varphi(p)时,a^b\equiv a^{b \bmod \varphi(p)+\varphi(p)}\pmod p.
b≥φ(p)时,ab≡abmodφ(p)+φ(p)(modp).
证明在这里
注意
b
<
φ
(
p
)
b<\varphi(p)
b<φ(p) 时不能使用扩展欧拉定理
5.后记
P
5091
【
模
板
】
欧
拉
定
理
\rm P5091\ 【模板】欧拉定理
P5091 【模板】欧拉定理 (注意这个是拓展欧拉定理)
P
2158
[
S
D
O
I
2008
]
仪
仗
队
\rm P2158\ [SDOI2008]仪仗队
P2158 [SDOI2008]仪仗队
这两道题还是比较裸的,自己去切吧~
这篇博文的撰写参考了很多资料,在这里无法一一列出,但依然表示十分感谢。
如果您发现了一些错误,最好上洛谷私信我的号(
U
38785
o
r
U
93465
\rm U38785\ or\ U93465
U38785 or U93465),将会在1天内回复。在优快云反馈只能保证30天内回复,在其它场合反馈只保证365天内回复
代码的话仅供参考,早期作品写得还是比较烂的。。你管一个月前叫早期?
6.例题
2020 / 3 / 17 2020~/~3~/~17 2020 / 3 / 17 新增板块
例题:洛谷P2568 GCD
题意:求
∑
p
∈
p
r
i
m
e
∑
i
=
1
n
∑
j
=
1
n
[
(
i
,
j
)
=
p
]
\sum\limits_{p\in \rm{prime}}\sum\limits_{i=1}^n\sum\limits_{j=1}^n[(i,j)=p]
p∈prime∑i=1∑nj=1∑n[(i,j)=p]
这个变形也算比较套路的,所以放个例题示范一下操作方法
我们知道
p
p
p 的枚举是不可避免的,所以我们先把它放在一边
首先套路式地把gcd变成互质
∑
i
=
1
n
/
p
∑
j
=
1
n
/
p
[
(
i
,
j
)
=
1
]
\sum\limits_{i=1}^{n/p}\sum\limits_{j=1}^{n/p}[(i,j)=1]
i=1∑n/pj=1∑n/p[(i,j)=1] (/
表示整除)
可以将它变形为
(
∑
i
=
1
n
/
p
(
2
∑
j
=
1
i
[
(
i
,
j
)
=
1
]
)
)
−
1
\left(\sum\limits_{i=1}^{n/p}\left(2\sum\limits_{j=1}^i[(i,j)=1]\right)\right)-1
(i=1∑n/p(2j=1∑i[(i,j)=1]))−1
-1
是因为
i
=
j
i=j
i=j 时有重复枚举,发现这时只有
i
=
j
=
1
i=j=1
i=j=1 时它们互质,因此答案只需-1
发现里面的求和式已经符合欧拉函数的定义了
那么最后一步把式子变成
(
2
∑
i
=
1
n
/
p
φ
(
i
)
)
−
1
\left(2\sum\limits_{i=1}^{n/p}\varphi(i)\right)-1
(2i=1∑n/pφ(i))−1
我们可以先预处理出
φ
\varphi
φ 的前缀和,然后就能实现
Θ
(
1
)
\Theta(1)
Θ(1) 计算
外面再套个枚举
p
p
p 的循环就行了
时间复杂度瓶颈其实在预处理上 为
Θ
(
n
)
\Theta(n)
Θ(n)
代码对式子又有一些小改动 不过不影响大致思路
#include<cstdio>
const int N=10000010;
int n,t,cnt,p[500000],v[N],phi[N]; long long S,s[N];
int main() {
scanf("%d",&n); phi[1]=s[1]=1;
for (int i=2; i<=n; ++i) {
v[i]||(p[++cnt]=i,phi[i]=i-1);
for (int j=1; j<=cnt&&1ll*p[j]*i<=n; ++j) {
v[t=p[j]*i]=1;
if (i%p[j]) phi[t]=phi[i]*(p[j]-1);
else { phi[t]=phi[i]*p[j]; break; }
}
s[i]=s[i-1]+phi[i];
}
for (int i=1; i<=cnt; ++i) S+=s[n/p[i]];
printf("%lld\n",(S<<1)-cnt);
return 0;
}