欧拉函数与欧拉定理
欧拉函数
定义
欧拉函数,即 \(\varphi(n)\),表示的是小于等于 \(n\) 的和 \(n\) 互质的数的个数。
当 \(n\) 是质数的时候,显然有 \(\varphi(n)=n-1\)。
性质
性质1
欧拉函数是积性函数。
积性函数:积性函数是指对于所有互质的整数 \(a\) 和 \(b\) 都有性质 \(f(ab)=f(a)f(b)\) 的数论函数,例如欧拉函数 \(\varphi(n)\),莫比乌斯函数 \(\mu(n)\),因数个数函数 \(d(n)\)。
完全积性函数:对于任意整数 \(a\) 和 \(b\) 有性质 \(f(ab)=f(a)f(b)\) 的数论函数。
积性函数的性质:若 \(f(n)\) 和 \(g(n)\) 都是积性函数,那么以下函数也为积性函数:
\(h(x)=f(x^p)\),\(h(x)=f^p(x)\),\(h(x)=f(x)g(x)\),\(h(x)=\sum\limits_{d|x}f(d)g(\dfrac{x}{d})\)。
对于欧拉函数,如果有 \(gcd(a,b)=1\),那么 \(\varphi(a\times b)=\varphi(a)\times \varphi(b)\)。
特别地,当 \(n\) 是奇数时有 \(\varphi(2n)=\varphi(n)\)。
性质2
\(\begin{aligned}n=\sum\limits_{d|n}\varphi(d)\end{aligned}\)。
此性质为欧拉反演的定义。
证明:
显然对于一个 \(i(1\le i\le n)\),\(i\) 与 \(n\) 的 \(gcd\) 都是唯一的。由此可得: \(\begin{aligned}n=\sum\limits_{d|n}\sum\limits_{i=1}^n[gcd(i,n)=d]\end{aligned}\)。
将公式变形,得到:\(\begin{aligned}n=\sum\limits_{d|n}\sum\limits_{i=1}^n[d\ |\ i,gcd(\dfrac{i}{d},\dfrac{n}{d})=1]\end{aligned}\)。
发现内层循环就是求在 \(\dfrac{n}{d}\) 的范围内与其互质的数的个数,即 \(\varphi(\dfrac{n}{d})\)。因此可将公式变形为 \(\begin{aligned}n=\sum\limits_{d|n}\varphi(\dfrac{n}{d})\end{aligned}\)。
由于 \(d\ |\ n\),因此 \(d\) 与 \(\dfrac{n}{d}\) 一一对应。因此可得到最终公式 \(\begin{aligned}n=\sum\limits_{d|n}\varphi(d)\end{aligned}\)。
证毕。
性质3
若 \(n=p^k\),其中 \(p\) 是质数,那么 \(\varphi(n)=p^k-p^{k-1}\)。
证明:相当于找在 \(\le p^k\) 中有多少个数是 \(p\) 的倍数,即 \(\dfrac{p^k}{p}\),即 \(p^{k-1}\),那么 \(\varphi(n)=p^k-p^{k-1}\)。证毕。
性质4
由整数的惟一分解定理,设 \(\begin{aligned}n=\prod\limits_{i=1}^s p_i^{\alpha_i}\end{aligned}\),其中 \(p_i\) 是质数,由 \(\begin{aligned}\varphi(n)=n\times \prod\limits_{i=1}^s \dfrac{p_i-1}{p_i}\end{aligned}\)。
惟一分解定理有多种指代意义:整数惟一分解定理,多项式惟一分解定理,交的惟一分解定理,乘积的惟一分解定理。
整数的惟一分解定理,又称算术分解定理,是数论的重要理论之一。该定理可以表述为:对于任意一个大于 \(1\) 的整数 \(n\) 都可以分解为若干个素因数的连乘积,如果不计各个素因数的顺序,那么这种分解是唯一的。
即若有 \(n>1\),则有 \(n=p_1^{\alpha_1}p_2^{\alpha_2}…p_k^{\alpha_k}\),其中 \(p_i\) 为素数,\(\alpha_i\) 为正整数。
证明:由整数的惟一分解定理与性质 \(3\),可得:\(\begin{aligned}\varphi(n)=\prod\limits_{i=1}^s \varphi(p_i^{\alpha_i})=\prod\limits_{i=1}^s p_i^{\alpha_i}\times (1-\dfrac{1}{p_i})=n\times \prod\limits_{i=1}^s (1-\dfrac{1}{p_i})=n\times \prod\limits_{i=1}^s \dfrac{p_i-1}{p_i}\end{aligned}\)。
求欧拉函数
求单个数的欧拉函数值
例题:SP4141 ETF - Euler Totient Function
根据性质 \(4\) 暴力求解即可,即质因数分解。有能力的可以学习 Pollard Rho 算法优化(等没事干了再学,别点偏了)。
纷总总兮九州,何寿天兮在予。
#include <bits/stdc++.h>
using namespace std;
int T,n,ans;
int main(){
scanf("%d",&T);while(T --){
scanf("%d",&n);ans = n;
for(int i = 2;i * i <= n;i ++){
if(n % i == 0){
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
printf("%d\n",ans);
}
return 0;
}
线性筛法求多个数的欧拉函数值
我们先回顾一下线性筛法筛质数。我们筛质数的思路即是,保证每一个合数都只被筛到一次,并且只被它的最小质因子筛到:
愿世间不再有无归之人。
void getPrime(){
int cnt = 0;vis[1] = 1;
for(int i = 2;i <= n;i ++){
if(vis[i] == 0) Prime[++cnt] = i;
for(int j = 1;j <= cnt and i * Prime[j] <= n;j ++){
vis[i * Prime[j]] = 1;
if(i % Prime[j] == 0) break;
}
}
}
那么在线性求欧拉函数的得时候,基本框架还是上面的东西,不过我们需要另外处理一些东西:
我们令 \(a\) 是 \(n\) 的最小质因子,\(n'=\dfrac{n}{a}\),那么线性筛的过程中 \(n\) 会通过 \(n=n'\times a\) 的形式筛掉。
如果说 \(n'\bmod a=0\),那么 \(n'\) 的所有质因子就和 \(n\) 一样。那么 \(\begin{aligned}\varphi(n)=n\times \prod\limits_{i=1}^s \dfrac{p_i-1}{p_i}=a\times n'\times \prod\limits_{i=1}^s\dfrac{p_i-1}{p_i}=a\times \varphi(n')\end{aligned}\)。
否则,此时 \(n'\) 和 \(a\) 就是互质的,那么根据性质一,可得 \(\varphi(n)=\varphi(a)\times \varphi(n')=(a-1)\times \varphi(n')\)。
司命者,当无情,有情即孽。
void getPhi(){
int cnt = 0;vis[1] = 1;phi[1] = 1;
for(int i = 2;i <= n;i ++){
if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1;
for(int j = 1;j <= cnt and i * Prime[j] <= n;j ++){
vis[i * Prime[j]] = 1;
if(i % Prime[j] == 0){
phi[i * Prime[j]] = phi[i] * Prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * phi[Prime[j]];//or phi[i] * (Prime[j] - 1)
}
}
}
习题
\(1.\) POJ 2478
线性求欧拉函数板子题。
跨过忘忧渡口,从此生死两隔。
#include <iostream>
#include <cstdio>
#define N 1000006
#define int long long
using namespace std;
int n,vis[N],phi[N],Prime[N],cnt;
void getPhi(){
vis[1] = 1;
for(int i = 2;i <= 1000000;i ++){
if(!vis[i]) phi[i] = i - 1,Prime[++cnt] = i;
for(int j = 1;j <= cnt && Prime[j] * i <= 1000000;j ++){
vis[i * Prime[j]] = 1;
if(i % Prime[j] == 0){
phi[i * Prime[j]] = phi[i] * Prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
}
}
for(int i = 2;i <= 1000000;i ++) phi[i] += phi[i - 1];
}
signed main(){
getPhi();while(cin >> n){
if(n == 0) return 0;
printf("%lld\n",phi[n]);
}
}
\(2.\) P2158 [SDOI2008] 仪仗队
容易发下左上角可右下角是对称的,因此我们劈开一半只看右下方。
我们以 C 君的位置为原点建立平面直角坐标系。容易得到结论,当且仅当 \(gcd(i,j)=1\) 的时候,这个人才不会被前面的人挡住。因此右下方的计算方法为,对于每一个 \(1\le i\le n-1\),求出其对应的 \(\varphi(i)\),相加,即转化为线性求欧拉函数值。然后稍微处理一下结果即可得到答案。并注意特判 \(n=1\) 的情况。
忘忧沼泽,是所有生命的归处,既结束,亦开始。
#include <bits/stdc++.h>
#define N 40005
using namespace std;
int n,vis[N],phi[N],cnt,num,Prime[N];
void Getphi(){
int cnt = 0;vis[1] = 1,phi[1] = 1,num = 1;
for(int i = 2;i <= n - 1;i ++){
if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1;
num += phi[i];
for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){
vis[i * Prime[j]] = 1;
if(i % Prime[j] == 0){
phi[i * Prime[j]] = phi[i] * Prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
}
}
}
int main(){
scanf("%d",&n);Getphi();
if(n == 1) printf("0");
else printf("%d",num * 2 + 1);
return 0;
}
\(3.\) P2568 GCD
思路很明显,我们先利用线性求欧拉函数计算出 \(gcd(x,y)=1\) 的,然后再分别让 \(x,y\) 乘上一个素数,那就满足了 \(gcd(x,y)\) 是一个素数。注意不要超过 \(n\),然后再自己进行一些细节处理即可。注意开 \(long\ long\)。
烈阳灼身,寒风刺骨,皆是无归之痛。
#include <bits/stdc++.h>
#define N 10000007
#define int long long
using namespace std;
int n,num,cnt,vis[N],phi[N],Prime[N];
void getPhi(){
vis[1] = 1,phi[1] = 1;
for(int i = 2;i <= n;i ++){
if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1;
for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){
vis[i * Prime[j]] = 1;
if(i % Prime[j] == 0){
phi[i * Prime[j]] = phi[i] * Prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
}
}
}
signed main(){
scanf("%lld",&n);getPhi();
for(int i = 2;i <= n;i ++) num += (upper_bound(Prime + 1,Prime + cnt + 1,n / i) - Prime - 1) * phi[i];
printf("%lld",num * 2 + cnt);
return 0;
}
\(4.\) P2303 [SDOI2012] Longge 的问题
本题即为欧拉反演,即性质 \(2\) 的应用。
推导:
令答案为 \(ans\)。我们对 \(gcd(i,n)\) 进行欧拉反演,得 \(\begin{aligned}ans=\sum\limits_{i=1}^n\sum\limits_{d|gcd(i,n)}\varphi(d)\end{aligned}\)。
观察到,若 \(d\ |\ gcd(i,n)\),那么满足它的充要条件为 \(d\ |\ i\) 且 \(d\ |\ n\)。因此得到 \(\begin{aligned}ans=\sum\limits_{i=1}^n\sum\limits_{d|i,d|n}\varphi(d)\end{aligned}\)。
将内层循环移至外层,得到 \(\begin{aligned}ans=\sum\limits_{d|n}\sum\limits_{i=1}^n\varphi(d)[d\ |\ i]\end{aligned}\)。紧接着得到 \(\begin{aligned}ans=\sum\limits_{d|n}\varphi(d)\sum\limits_{i=1}^n [d\ |\ i]\end{aligned}\)。
易得,在 \(n\) 以内 \(d\ |\ i\) 的个数就是 \(\dfrac{n}{d}\)。因此我们最终得到 \(\begin{aligned}ans=\sum\limits_{d|n}\dfrac{n}{d}\varphi(d)\end{aligned}\)。
推导毕。
时间复杂度分析:
我们枚举因数的时间复杂度是 \(O(\sqrt{n})\),求一个因数的欧拉函数的复杂度是 \(O(\sqrt{n})\),令 \(S=\) 因数个数,那么总时间复杂度是 \(O(S\times \sqrt{n})\)。
根据惟一分解定理,我们对 \(n\) 进行质因数分解:\(n=p_1^{r_1}p_2^{r_2}…p_k^{r_k}\)。
当 \(n\) 的因数最多的时候,\(r_1=r_2=…=r_k=1\)。而在 \(2^{32}\) 的范围内,满足条件的最大的数为 \(223092870=2\times 3\times 5\times 7\times 11\times 13\times 17\times 19\times 23\)。
因此因数个数为 \(2^9\),因此本题的时间复杂度上线为 \(O(2^9\times \sqrt{n})\),足以通过此题。
天地之间,无法消散的执念,便化作恶灵。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,num;
int getPhi(int x){
int ans = x;
for(int i = 2;i * i <= x;i ++){
if(x % i == 0){
ans = ans / i * (i - 1);
while(x % i == 0) x /= i;
}
}
if(x > 1) ans = ans / x * (x - 1);
return ans;
}
void Decom(){
for(int i = 1;i * i <= n;i ++){
if(n % i == 0){
if(i * i == n) num += i * getPhi(i);
else num += (n / i) * getPhi(i) + i * getPhi(n / i);
}
}
}
signed main(){
scanf("%lld",&n);Decom();
printf("%lld",num);
return 0;
}
\(5.\) P2398 GCD SUM
本题只不过是上一个题稍微进阶一下,可以尝试自己推一下式子。
易得,原式 \(\begin{aligned}=\sum\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{d|gcd(i,j)}\varphi(d)=\sum\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{d|i,d|j}\varphi(d)=\sum\limits_{d=1}^n\varphi(d)\sum\limits_{i=1}^n\sum\limits_{j=1}^n[d\ |\ i][d\ |\ j]=\sum\limits_{d=1}^n\varphi(d)\sum\limits_{i=1}^n[d\ |\ i]\sum\limits_{j=1}^n[d\ |\ j]=\sum\limits_{d=1}^n\varphi(d)\lfloor\dfrac{n}{d}\rfloor^2\end{aligned}\)。
推导毕。
云梦之人相信,人死之后,会魂归天生木,再次归家。
#include <bits/stdc++.h>
#define N 100005
#define int long long
using namespace std;
int n,num,cnt,vis[N],phi[N],Prime[N];
void getPhi(){
vis[1] = 1,phi[1] = 1,num = n * n;
for(int i = 2;i <= n;i ++){
if(vis[i] == 0) Prime[++cnt] = i,phi[i] = i - 1;
num += phi[i] * (n / i) * (n / i);
for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){
vis[i * Prime[j]] = 1;
if(i % Prime[j] == 0){
phi[i * Prime[j]] = phi[i] * Prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
}
}
}
signed main(){
scanf("%lld",&n);getPhi();
printf("%lld",num);
return 0;
}
\(6.\) P2350 [HAOI2012] 外星人
本题是对性质 \(4\) 理解的一个考察运用,当然直观的,可以看下面提示的那个式子,对做本题来讲更为清晰:\(\varphi(\prod\limits_{i=1}^mp_i^{q_i})=\prod\limits_{i=1}^m(p_i-1)\times p_i^{q_i-1}\)。
我们知道,在质数中只有 \(\varphi(2)=1\),那么对于本题来讲,\(\varphi^x(2^n)\) 有最小的 \(x=n\)。因为每一次操作最多只能让一个 \(2\) 变成 \(1\),并且随即也会有其它的质数减一后再生成一些新的 \(2\)。因此我们的思路就是,看看一共能出来多少个 \(2\),就是答案。令 \(f(i)\) 表示 \(i\) 这个数在整个过程中可以出来多少个 \(2\),那么我们可以得到状态转移方程:
\(f(i)=\begin{cases}f(i-1)&i\in Prime\\f(x)+f(y)&x\times y=i\end{cases}\)。
显然,如果 \(i\) 是质数,那么这一次操作只会让 \(i\) 变成 \(i-1\),出来的 \(2\) 的个数就是 \(i-1\) 出来的 \(2\) 的个数。第二行的式子也显然。
但是我们要注意一个细节问题:如果刚开始这个数 \(N\) 没有因子 \(2\),那么需要让最终答案 \(+1\),因为第一次操作没有 \(2\) 的减少,只有 \(2\) 的生成。
那么,除了上面的特例外,其他情况下,每一次操作一定会减少一个 \(2\),没减少一个 \(2\) 也一定对应着一个操作。因此对于每一个质因子,贡献即为 \(f_{p_i}\times q_i\)。
回首万里,故人长绝。
#include <bits/stdc++.h>
#define N 100005
#define int long long
using namespace std;
int T,num,f[N],vis[N],Prime[N],cnt,m;
void getThrough(){
f[1] = 1,vis[1] = 1;
for(int i = 2;i <= 100000;i ++){
if(!vis[i]) f[i] = f[i - 1],Prime[++cnt] = i;
for(int j = 1;j <= cnt and Prime[j] * i <= 100000;j ++){
vis[i * Prime[j]] = 1;
f[i * Prime[j]] = f[i] + f[Prime[j]];
if(i % Prime[j] == 0) break;
}
}
}
signed main(){
getThrough();scanf("%lld",&T);while(T --){
scanf("%lld",&m);num = 1;
for(int i = 1,p,q;i <= m;i ++){
scanf("%lld%lld",&p,&q);if(p == 2) num --;
num += f[p] * q;
}
printf("%lld\n",num);
}
return 0;
}
欧拉定理
前置 1 简化剩余系的概念
剩余类
定义
剩余类,亦称同余类。设模为 \(n\),则根据余数可将所有的整数分为 \(n\) 类,即 \(0\sim n-1\),把所有与整数 \(a\) 模 \(n\) 同余的整数构成的集合叫做模 \(n\) 的一个剩余类,记作 \([a]\)。并把 \(a\) 叫作剩余类 \([a]\) 的一个代表元。
形式化的,即以 \(C_r(0\le r\le n-1)\) 表示所有形如 \(q\times n+r,q\in \mathbb{Z}\),则 \(C_0,C_1,…,C_{n-1}\) 称为 \(p\) 的剩余类。
性质
\(1.\) 每一个整数都恰好包含在某一个剩余类 \(C_i(0\le i\le n-1)\) 里面。
\(2.\) 两个整数 \(x,y\) 属于同一类的充要条件是 \(x\equiv y\pmod{n}\)。
完全剩余系
定义
从模 \(n\) 的每个剩余类中各取一个数,得到一个由 \(n\) 个数组成的集合,叫做模 \(n\) 的一个完全剩余系。最常用的完全剩余系是 \({0,1,…,n-1}\)。
举例:模 \(4\) 的一个完全剩余系为 \(\{0,1,2,3\}\),也可以是 \(\{4,5,-2,11\}\)。
性质
\(1.\) 对于 \(n\) 个整数,其构成模 \(n\) 的完全剩余系等价于其关于模 \(n\) 两两不同余。
\(2.\) 若 \(a_i(1\le i\le n)\) 构成模 \(n\) 的一个完全剩余系,则对于任意的 \(k,m\in \mathbb{Z}\) 且 \(gcd(k,n)=1\),有 \(k\times a_i+m(1\le i\le n)\) 也为模 \(n\) 的完全剩余系。
证明
使用反证法。假设存在 \(k\times a_i\equiv k\times a_j(1\le i<j\le n)\pmod{n}\),则 \(n\ |\ k\times(a_i-a_j)\)。
\(\because gcd(k,n)=1\),\(\therefore n\ |\ (a_i-a_j)\),\(\therefore a_i\equiv a_j\pmod{n}\)。
但是 \(a_i\not\equiv a_j\pmod{n}\),因此假设不成立。因此 \(k\times a_i(1\le i\le n)\) 两两不同余,\(+m\) 后显然成立。
证毕。
\(3.\) 若 \(a_i(1\le i\le n)\) 构成模 \(n\) 的一个完全剩余系,\(b_i(1\le i\le m)\) 构成模 \(m\) 的一个完全剩余系,且满足 \(gcd(n,m)=1\) 时,有 \(m\times a_i+n\times b_j(1\le i\le n,1\le j\le m)\) 也构成模 \(n\times m\) 的完全剩余系。
证明
依旧使用反证法。假设存在 \(m\times a_{i_1}+n\times b_{j_1}\equiv m\times a_{i_2}+n\times b_{j_2}\pmod{n\times m}(1\le i_1<i_2\le n,1\le j_1<j_2\le m)\)。
则 \(n\times m\ |\ m\times(a_{i_1}-a_{i_2})+n\times(b_{j_1}-b_{j_2})\),显然有 \(n\ |\ m\times(a_{i_1}-a_{i_2})+n\times(b_{j_1}-b_{j_2})\)。
\(\because gcd(n,m)=1\),\(\therefore n\ |\ (a_{i_1}-a_{i_2})\),即 \(a_{i_1}\equiv a_{i_2}\pmod{n}\)。同理可证得 \(b_{j_1}\equiv b_{j_2}\pmod{m}\)。
但是二者均不满足条件,因此假设不成立。因此集合内元素两两不同余。
证毕。
简化剩余系
定义
简化剩余系也称既约剩余系或缩系,是 \(n\) 的完全剩余系中与 \(n\) 互素的数构成的子集,如果模 \(n\) 的一个剩余类里所有数都与 \(n\) 互素,就把它叫做与模 \(n\) 互素的剩余类。在与模 \(n\) 互素的全体剩余类中,从每一个类中各任取一个数作为代表组成的集合,叫做模 \(n\) 的一个简化剩余系。
举例:\(12\) 的一个简化剩余系是 \(1,5,7,11\)。
性质
\(1.\) 若 \(a_i(1\le i\le n)\) 构成模 \(n\) 的一个简化剩余系,则对于任意的 \(k,l\in \mathbb{Z}\) 且 \(gcd(k,n)=1\),有 \(k\times a_i+l\times n(1\le i\le n)\) 也为模 \(n\) 的简化剩余系。
证明比较显然。\(gcd(k,n)=1,gcd(a_i,n)=1\),因此 \(gcd(k\times a_i,n)=1\),加上 \(l\times n\) 后同理。证毕。
\(2.\) 若 \(a_i(1\le i\le n)\) 构成模 \(n\) 的一个简化剩余系,\(b_i(1\le i\le m)\) 构成模 \(m\) 的一个简化剩余系,且满足 \(gcd(n,m)=1\) 时,有 \(m\times a_i+n\times b_j(1\le i\le n,1\le j\le m)\) 也构成模 \(n\times m\) 的简化剩余系。
前置 2 关于费马小定理的证明
费马小定理:若 \(p\) 为素数,\(gcd(a,p)=1\),那么有 \(a^{p-1}\equiv 1\pmod{p}\)。
证明:
设一个质数为 \(p\),我们取一个不为 \(p\) 的倍数的数 \(a\)。
构造一个序列 \(A=\{1,2,…,p-1\}\)。虽然 \(A\) 序列里面没有 \(0\),但是我们把它当做模 \(p\) 意义下的一个完全剩余系看待。
那么根据完全剩余系的性质 \(2\),可得 \(a\times A_i(1\le i\le p-1)\) 也为模 \(p\) 意义下的完全剩余系,当然也是没有 \(0\)。因此 \(A_i\) 与 \(a\times A_i\) 一一对应。
我们设 \(f=(p-1)!\),那么 \(f\equiv a\times A_1\times a\times A2\times …\times a\times A_{p-1}\pmod{p}\),则 \(a^{p-1}\times f\equiv f\pmod{p}\),则 \(a^{p-1}\equiv 1\pmod{p}\)。
证毕。
定义
若 \(gcd(a,m)=1\),则 \(a^{\varphi(m)}\equiv 1\pmod{m}\)。
证明:
与费马小定理类似。同样构造一个与 \(m\) 互质的序列。
设 \(r_1,r_2,…,r_{\varphi(m)}\) 为模 \(m\) 意义下的一个简化剩余系,\(\because gcd(a,m)=1\),\(\therefore a\times r_1,a\times r_2,…,a\times r_{\varphi(m)}\) 也是模 \(m\) 意义下的一个简化剩余系。
因此 \(r_1r_2…r_{\varphi(m))}\equiv ar_1\times ar_2\times …\times ar_{\varphi(m)}\)。两边同时约去 \(r_1r_2…r_{\varphi(m))}\),得到 \(a^{\varphi(m)}\equiv 1\pmod{m}\)。
因此在满足 \(gcd(a,m)=1\) 时,\(a^{\varphi(m)}\equiv 1\pmod{m}\)。
证毕。
然后我们可以通过这个结论证明费马小定理。由于当 \(p\) 为素数时,\(\varphi(p)=p-1\),带入即可得到费马小定理。
扩展欧拉定理
定义
\(f(x)=a^b\equiv\begin{cases}a^{b\bmod\varphi(m)}&gcd(a,m)=1\\a^b&gcd(a,m)\ne 1,b\le\varphi(m)\\a^{(b\bmod\varphi(m))+\varphi(m)}&gcd(a,m)\ne 1,b\ge \varphi(m)\end{cases}\pmod{m}\)。
证明不会。
例题 P5091 【模板】扩展欧拉定理
一个很重要的步骤就是边输入边取模。
于荒林中独行夜路,就算心中志忑,也不必回头。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a,m,b,phi,flag;
inline int read()
{
int x = 0,f = 1;char ch = getchar();
while(ch < '0' or ch > '9'){
if (ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' and ch <= '9'){
x = x * 10 + ch - 48;
if(x >= phi) flag = 1;
x %= phi;ch = getchar();
}
return x * f;
}
int ksm(int a,int b){
int res = 1;
while(b){
if(b & 1) res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
int getPhi(int n){
int ans = n;
for(int i = 2;i * i <= n;i ++){
if(n % i == 0){
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
return ans;
}
signed main(){
scanf("%lld%lld",&a,&m);a %= m;
phi = getPhi(m);b = read();
b += (flag == 1 ? phi : 0);
printf("%lld",ksm(a,b));
return 0;
}
习题
\(1.\) P5221 Product
原式 \(\begin{aligned}=\prod\limits_{i=1}^N\prod\limits_{j=1}^N\dfrac{i\times j}{gcd(i,j)^2}=\left(\prod\limits_{i=1}^N\prod\limits_{j=1}^Ni\times j\right)\times\left(\prod\limits_{i=1}^N\prod\limits_{j=1}^Ngcd(i,j)\right)^{-2}=(N!)^{2N}\times\left(\prod\limits_{d=1}^Nd^{\sum\limits_{i=1}^N\sum\limits_{j=1}^N[gcd(i,j)=d]}\right)^{-2}=(N!)^{2N}\times \left(\prod\limits_{d=1}^Nd^{\sum\limits_{i=1}^{\left\lfloor N/d\right\rfloor}\sum\limits_{j=1}^{\left\lfloor N/d\right\rfloor}[gcd(i,j)=d]}\right)\end{aligned}\)。
后者的指数我们可以使用欧拉函数的前缀和解决,但是空间 \(7.81MB\) 大概是能开 \(2\) 个 \(N=10^6\) 大小的 \(int\) 数组,我们正常写有三个数组,一个是 \(vis\) 数组,是判断数是否是质数的数组,我们可以把它改成 \(bool\) 型数组;还有一个是 \(Prime\) 数组,是记录质数的,不过在 \(10^6\) 之内大概只有不到 \(8\times 10^4\) 个质数,因此就开这么大就行;还有一个是 \(phi\) 数组,显然我们是需要开 \(long\ long\) 的,但是开了 \(long\ long\) 空间就超了,因此我们用欧拉定理优化一下。由于这个模数是质数,因此我们直接让指数取模 \(\varphi(mod)=mod-1\) 即可,这个时候我们就能开 \(int\) 数组存了。
若要终结枯灾,唯有九神巫同补天穹。
#include <bits/stdc++.h>
#define N 1000006
#define ll long long
#define MOD 104857601
using namespace std;
bool vis[N];
ll ans = 1,num = 1,sum;
int n,cnt,phi[N],Prime[80004];
void getPhi(){
vis[1] = 1,phi[1] = 1;
for(int i = 2;i <= n;i ++){
if(!vis[i]) Prime[++cnt] = i,phi[i] = i - 1;
for(int j = 1;j <= cnt and Prime[j] * i <= n;j ++){
vis[Prime[j] * i] = 1;
if(i % Prime[j] == 0){
phi[i * Prime[j]] = phi[i] * Prime[j];
break;
}
phi[i * Prime[j]] = phi[i] * (Prime[j] - 1);
}
}
for(int i = 2;i <= n;i ++) phi[i] = (phi[i] * 2 + phi[i - 1]) % (MOD - 1);
}
ll ksm(ll a,ll b){
ll res = 1;
while(b){
if(b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
signed main(){
scanf("%d",&n);getPhi();
for(int i = 1;i <= n;i ++){
ans = ans * i % MOD;
num = num * ksm(i,phi[n / i]) % MOD;
}num = num * num % MOD;
sum = ksm(ans,2 * n) * ksm(num,MOD - 2) % MOD;
printf("%lld",sum);
return 0;
}