欧拉函数与欧拉定理

欧拉函数

定义

欧拉函数,即 \(\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;
}
posted @ 2024-03-28 15:28  Joy_Dream_Glory  阅读(41)  评论(0编辑  收藏  举报