【数论】费马小定理
文章目录
- 一、同余
- 1. 同余的定义
- 2. 同余的性质
- 二、费马小定理
- 1. 费马小定理的内容
- 2. 费马小定理的证明(趣味版)
- 3. 求解乘法逆元
- (1) 乘法逆元
- (2) 费马小定理 + 快速幂求逆元
- (3) 应用
- 三、OJ 练习
- 1. 序列求和 ⭐
- (1) 解题思路
- (2) 代码实现
- 2. 水上舞者索尼娅 ⭐⭐
- (1) 解题思路
- (2) 代码实现
一、同余
1. 同余的定义
基本定义:设 m m m 是正整数,如果 a , b a,b a,b 的差 a − b a-b a−b 被 m m m 整除即 a − b = q m a-b=qm a−b=qm,就称 a , b a, \ b a, b 关于模 m m m 同余,或简称同余。记为
a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm)
简单来说就是如果 a a a 和 b b b 除以 m m m 的余数相同,那么 a , b a, \ b a, b 就关于模 m m m 同余。
2. 同余的性质
若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), c ≡ d ( m o d m ) c\equiv d\pmod m c≡d(modm),则 a ± c ≡ b ± d ( m o d m ) a\pm c\equiv b\pm d\pmod m a±c≡b±d(modm);
若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), c ≡ d ( m o d m ) c\equiv d\pmod m c≡d(modm),则 a c ≡ b d ( m o d m ) ac\equiv bd\pmod m ac≡bd(modm);
若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm), n ∈ N n\in N n∈N,则 a n ≡ b n ( m o d m ) a^n\equiv b^n\pmod m an≡bn(modm);
若 a c ≡ b c ( m o d m ) ac\equiv bc\pmod m ac≡bc(modm),且 c c c 与 m m m 互质,则 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm);
a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm) ⇔ \Leftrightarrow ⇔ a − b = k m a-b=km a−b=km。
可见同余的两侧的数字同时加、减、乘一个数字没有限制,但是同时除以一个数时有一定的限制。
二、费马小定理
1. 费马小定理的内容
费马小定理:如果 p p p 是一个质数,对于任意整数 a a a,都有
a p ≡ a ( m o d p ) a^p\equiv a\pmod p ap≡a(modp)
此外我们还会见到另一种形式:如果 p p p 是一个质数, a a a 不能被 p p p 整除,那么有
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap−1≡1(modp)这是由于同余的性质,如果两边同时除以 a a a 的话,必须要满足 a a a 和 p p p 互质,由于 p p p 是质数,所以等同余 a a a 不能被 p p p 整除。
2. 费马小定理的证明(趣味版)
证明:定理的证明千篇一律,有趣的思维万里挑一。这里我想用一种有趣的方式进行证明,过程如下:
假设现在有一串珠子形成了一个环,环上珠子的数量是 p p p( p p p是素数),现在我有 a a a 种颜色,我要给这个环上的每一个珠子进行上色。根据小学知识,每个珠子的上色情况都有 a a a 种,因此总共有 a p a^p ap 种上色方式。
假如我要用至少两种颜色进行上色,总共就有 ( a p − a ) (a^p-a) (ap−a) 种上色方式。我们现在要证明的是 a p − a ≡ 0 ( m o d p ) a^p-a\equiv0\pmod p ap−a≡0(modp),也就是证明 a p − a a^p-a ap−a 这个式子能够被 p p p 整除,即证明 a p − a = k p a^p-a=kp ap−a=kp 。
又回到这个给环上色问题上,问题就可以转化为:如果我们可以把至少用两种颜色这一情况分成 k k k 组,每组都刚好有 p p p 种上色方式
那我们就可以证明 a p − a a^p-a ap−a 是 p p p 的 k k k 倍了!

总共 a p a^p ap 种上色方式 { 一种颜色 : a 至少两种颜色: a p − a { p 种 p 种 ⋮ p 种 \begin{cases}一种颜色:a\\\\\\至少两种颜色:a^p-a\begin{cases}p种\\p种\\\vdots \\p种\end{cases}\end{cases} ⎩ ⎨ ⎧一种颜色:a至少两种颜色:ap−a⎩ ⎨ ⎧p种p种⋮p种
为了方便,我们把可以通过旋转得到一模一样的两种上色方式称为"同款",如下:

现在我们把 ”同款“ 的上色方式放在一组里面,由上图不难发现,当我们关注红色的珠子时,当它绕着旋转一圈后,它把每个位置都经过了一边。也就是说,当我们把这个环进行旋转时,它所出现的不同上色方式恰好就是这个环上的珠子的个数,也就是 p p p。也就说明了同款里不同的上色方式就是 p p p 种。即我们确实可以把 ( a p − a ) (a^p-a) (ap−a) 种上色方式分成若干个 p p p 种上色方式。
BUT!这好像有个bug啊…万一在旋转过程中出现了两个一模一样的上色咋办?请看VCR:

别急!这个时候, p p p 是素数的条件就很关键了。既然是素数,就说明它只能被1和它本身整除,所以说实际上是不可能出现像上面那样有几个完整周期排列的。只要不是由几个完整的周期依次排列成环的,就不会出现像上面那样转了不到一圈就完全重合的现象。
综上,我们就说明了在 ( a p − a ) (a^p-a) (ap−a) 种上色方式中,我们可以把它分成 k k k 组,每组都恰好有 p p p 种上色方式。即
a p − a = k p ⇕ a p − a ≡ 0 ( m o d p ) ⇕ a p ≡ a ( m o d p ) a^p-a=kp\\ \Updownarrow\\ a^p-a\equiv0\pmod p\\ \Updownarrow\\ a^p\equiv a\pmod p\\ ap−a=kp⇕ap−a≡0(modp)⇕ap≡a(modp)
费马小定理,证毕!
3. 求解乘法逆元
(1) 乘法逆元
- 定义
对于正整数 a a a 和 p p p,若有
a x ≡ 1 ( m o d p ) ax\equiv1\pmod p ax≡1(modp)
那么把这个同余方程中的 x x x 的解叫做 a a a 模 p p p 的乘法逆元,简称逆元,记作 a − 1 a^{-1} a−1。
例如 8 x ≡ 1 ( m o d 5 ) 8x \equiv 1 \pmod 5 8x≡1(mod5) 中, x = 2 , 7 , ⋯ x = 2, 7, \cdots x=2,7,⋯ 。那么解出来的这些 x x x 就是 8 8 8 模 5 5 5 的乘法逆元。
- a a a 存在乘法逆元的充要条件是 a a a 和 p p p 互质
要想说明 a x ≡ 1 ( m o d p ) ax\equiv1\pmod p ax≡1(modp) 存在这么一个 x x x ,可以进行一个简单的变形。因为是对 a x ax ax 取模,相当于是把 a x ax ax 减掉了 k k k 个 p p p 最终剩下了 1 1 1,相当于可以写成 a x − k p = 1 ax-kp=1 ax−kp=1, k k k 是一个整数,这里等价于 a x + p y = 1 ax+py=1 ax+py=1。而根据裴蜀定理,方程 a x + p y = 1 ax+py=1 ax+py=1 有解的充要条件是 gcd ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1。
(2) 费马小定理 + 快速幂求逆元
a a a 存在乘法逆元的充要条件是 a a a 与 p p p 互质,而**当模数 p p p 是质数时,**满足费马小定理的条件,由费马小定理:
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap−1≡1(modp)
提出一个 a a a 出来,可以得到
a ⋅ a p − 2 ≡ 1 ( m o d p ) a\cdot a^{p-2}\equiv1\pmod p a⋅ap−2≡1(modp)
而这个式子就恰好满足了我们希望的形式,因此可以看出, a a a 的逆元就是 a p − 2 a^{p-2} ap−2,而 a p − 2 a^{p - 2} ap−2 则可用快速幂进行求解。
注:当 p p p 不是质数的时候,需要使用其他方法进行求解,例如扩展欧几里得算法。
#include <iostream>using namespace std;typedef long long LL;// 必须要保证 a, p 互质,且 p 为质数
LL qpow(LL a, LL b, LL p)
{LL ret = 1;while(b){if(b & 1) ret = ret * a % p;b >>= 1;a = a * a % p;}return ret;
}int main()
{LL x, p; cin >> n >> p;cout << qpow(x, p - 2, p) << endl; // 打印 x 在模 p 意义下的乘法逆元return 0;
}
时间复杂度与快速幂一致,为 O ( log n ) O(\operatorname{log}n) O(logn)。
(3) 应用
当我们求解 $(20 \div 3)\bmod 5 $ 这种带模数的运算时,由于 20 ÷ 3 20\div 3 20÷3 是一个小数,我们没法直接求解,这个时候我们可以用逆元把除法变成乘法。这个问题等价于 ( 20 × 3 − 1 ) m o d 5 (20 \times 3^{-1}) \bmod 5 (20×3−1)mod5,注意这里的 3 − 1 3^{-1} 3−1 是乘法逆元,不是倒数。 3 3 3 在模 5 5 5 下的逆元是 2 2 2,因此原式等价于 ( 20 × 2 ) m o d 5 = 0 (20 \times 2)\bmod 5 = 0 (20×2)mod5=0。
三、OJ 练习
1. 序列求和 ⭐
【题目链接】
序列求和

(1) 解题思路
中学时我们学过, 1 2 + 2 2 + ⋯ + n 2 = n ( n + 1 ) ( 2 n + 1 ) 6 1^2 + 2^2 + \cdots + n^2 = \frac{n(n + 1)(2n + 1)}{6} 12+22+⋯+n2=6n(n+1)(2n+1),所以这道题本质上是在求右边这个分式的值然后对 ( 1 0 9 + 7 ) (10^9 + 7) (109+7) 取模的结果。如果我们直接求解,那么我们需要计算 n ( n + 1 ) ( 2 n + 1 ) n(n + 1)(2n + 1) n(n+1)(2n+1),由于 n n n 非常大,所以我们要边乘边取模,但是模完之后的数可能不是 6 6 6 的倍数了,因此我们不能用除以 6 6 6 来运算最终结果,所以需要用乘法逆元把除法转换为乘法。原式等价于求解
n ( n + 1 ) ( 2 n + 1 ) × 6 − 1 m o d ( 1 0 9 + 7 ) n(n + 1)(2n + 1) \times 6^{-1} \bmod (10^9 + 7) n(n+1)(2n+1)×6−1mod(109+7)
求解 6 6 6 在模 ( 1 0 9 + 7 ) (10^9 + 7) (109+7) 下的逆元采用费马小定理 + 快速幂即可。
(2) 代码实现
#include<iostream>using namespace std;typedef long long LL;const int p = 1e9 + 7;LL n;// 快速幂
LL qpow(LL a, LL n, LL p)
{LL ret = 1;while(n){if(n & 1) ret = ret * a % p;n >>= 1;a = a * a % p;}return ret;
}// 费马小定理求解 x 模 p 下的逆元
LL rev(LL x, LL p)
{return qpow(x, p - 2, p);
}int main()
{while(cin >> n){// 由于 n 可能非常大,所以每一步乘法运算后都需要取一次模防止溢出LL ans = ((((n % p) * ((n + 1) % p) % p) % p) * ((2 * n + 1) %p)) % p * (rev(6, p)) % p;cout << ans << endl;}return 0;
}
2. 水上舞者索尼娅 ⭐⭐
【题目链接】
P11465 水上舞者索尼娅 - 洛谷
【题目背景】
2024 年 12 月 18 日,《炉石传说》31.2.2 补丁上线,水上舞者索尼娅遭到削弱。现在,在这道题目中,你打开时空之门,回到了水索被削弱前的时候。
【题目描述】
你的场上有 k k k 个【水上舞者索尼娅】,你的手牌中有 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 n n n 根)】,你有无尽的法力值,你的手牌数量没有上限。
在你的场上有 k k k 个【水上舞者索尼娅】的情况下:
当你使用 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 x x x 根)】时,你将得到:
- k k k 张法力值消耗为 0 0 0 的【一串香蕉(还剩 x x x 根)】。
- 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 x − 1 x-1 x−1 根)】。(若 x = 1 x=1 x=1,则不会得到)
当你使用 1 1 1 张法力值消耗为 0 0 0 的【一串香蕉(还剩 x x x 根)】时,你将得到:
- 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 x − 1 x-1 x−1 根)】。(若 x = 1 x=1 x=1,则不会得到)
你一共可以使用多少次【一串香蕉】?
由于答案可能很大,你只需求出答案对 1 0 9 + 7 10^9+7 109+7 取模的余数。
【输入格式】
本题有多组数据。
第一行一个正整数 T T T,表示数据组数。
对于每组数据:
一行两个正整数 n , k n,k n,k。
【输出格式】
对于每组数据:
输出一行一个整数,表示答案对 1 0 9 + 7 10^9+7 109+7 取模的余数。
【示例一】
输入
3 2 2 3 1 12 34输出
12 14 178629506
【说明/提示】
1 ≤ T ≤ 1 0 5 1\le T\le10^5 1≤T≤105, 1 ≤ n , k ≤ 1 0 9 1\le n,k\le10^9 1≤n,k≤109。
样例解释:
对于第 1 1 1 组数据:
场上有 2 2 2 个【水上舞者索尼娅】,初始手牌中有 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 2 2 2 根)】。
使用: 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 2 2 2 根)】。
手牌:
- 2 2 2 张法力值消耗为 0 0 0 的【一串香蕉(还剩 2 2 2 根)】。
- 1 1 1 张法力值消耗为 1 1 1 的【一串香蕉(还剩 1 1 1 根)】。
使用: 2 2 2 张法力值消耗为 0 0 0 的【一串香蕉(还剩 2 2 2 根)】。
手牌:
- 3 3 3 张法力值消耗为 1 1 1 的【一串香蕉(还剩 1 1 1 根)】。
使用: 3 3 3 张法力值消耗为 1 1 1 的【一串香蕉(还剩 1 1 1 根)】。
手牌:
- 6 6 6 张法力值消耗为 0 0 0 的【一串香蕉(还剩 1 1 1 根)】。
使用: 6 6 6 张法力值消耗为 0 0 0 的【一串香蕉(还剩 1 1 1 根)】。
手牌:
- 空
共使用 1 + 2 + 3 + 6 = 12 1+2+3+6=12 1+2+3+6=12 张【一串香蕉】。
(1) 解题思路
我们把法力值消耗为 1 1 1 的【一串香蕉(还剩 n n n 根)】记为 ( 1 , n ) (1, n) (1,n),以及场上有 k k k 个【水上舞者索尼娅】。此时我们使用一次一串香蕉,根据题意,你会得到 k k k 个 ( 0 , n ) (0, n) (0,n) 和 一个 ( 1 , n − 1 ) (1, n - 1) (1,n−1),此时再把 k k k 个 ( 0 , n ) (0, n) (0,n) 全部使用,那么就会得到 k k k 个 ( 1 , n − 1 ) (1, n - 1) (1,n−1),这个时候,你会得到 k + 1 k + 1 k+1 个 ( 1 , n − 1 ) (1, n - 1) (1,n−1)。
(1, n) --使用k+1次--> (k + 1)个(1, n - 1)
那么以此类推,我们可以得出以下规律
(1, n) --使用k+1次--> (k + 1)个(1, n - 1) --使用(k+1)^2次--> (k + 1)^2个(1, n - 2) ...
所以我们最终就可以使用 ( k + 1 ) + ( k + 1 ) 2 + ⋯ + ( k + 1 ) n = ∑ i = 1 n ( k + 1 ) i (k + 1) + (k + 1)^2 + \cdots + (k + 1)^n = \sum\limits_{i = 1}^{n}(k + 1)^i (k+1)+(k+1)2+⋯+(k+1)n=i=1∑n(k+1)i 张【一根香蕉】。
根据等比数列求和公式再进行整理,最终答案就是 ( k + 1 ) n + 1 − k − 1 k m o d ( 1 0 9 + 7 ) \frac{(k+1)^{n + 1} - k - 1}{k} \bmod (10^9 + 7) k(k+1)n+1−k−1mod(109+7)。由于在模运算中出现了除法,所以等价于求解 [ ( k + 1 ) n + 1 − k − 1 ] × k − 1 m o d ( 1 0 9 + 7 ) [(k+1)^{n + 1} - k - 1]\times k^{-1} \bmod (10^9 + 7) [(k+1)n+1−k−1]×k−1mod(109+7)。
(2) 代码实现
#include<iostream>using namespace std;typedef long long LL;const int p = 1e9 + 7;
LL n, k;// 快速幂
LL qpow(LL a, LL n, LL p)
{LL ret = 1;while(n){if(n & 1) ret = ret * a % p;n >>= 1;a = a * a % p;}return ret;
}// 求解 x 模 p 下的逆元
LL inv(LL x, LL p)
{return qpow(x, p - 2, p);
}int main()
{int t;cin >> t;while(t--){cin >> n >> k;LL ans = (qpow(k + 1, n + 1, p) - k - 1) * inv(k, p);// 注意把答案补成最小非负整数cout << ((ans % p) + p) % p << endl;}
}

