
在算法竞赛的数论领域,费马小定理是解决模运算、乘法逆元等问题的 “金钥匙”。它看似简洁,却能破解模运算中除法不能直接取模的核心痛点,成为快速幂、组合数计算、序列求和等问题的核心支撑。本文将从同余性质切入,层层拆解费马小定理的原理、应用场景,详解乘法逆元的求解逻辑,手把手教你掌握从理论到实战的全流程,让你在模运算问题中轻松举一反三。下面就让我们正式开始吧!
在理解费马小定理之前,我们必须先掌握同余式的基本概念和性质 —— 这是数论中模运算的基础,也是费马小定理应用的前提。
若两个整数 a 和 b 除以正整数 m 的余数相同,则称 a 和 b 模 m 同余,记作a≡b(mod m)。例如,7 除以 3 余 1,4 除以 3 也余 1,因此7≡4(mod 3)。
同余的本质是:a−b能被 m 整除,即m∣(a−b)。这个定义看似简单,却能将复杂的整数运算转化为模意义下的简化运算,大幅降低计算难度。
同余式满足加、减、乘三种运算的封闭性,但不满足除法运算 —— 这也是后续需要乘法逆元的核心原因。具体性质如下:
举个反例:20≡2(mod 3)(20 mod 3=2,2 mod 3=2),但两边同时除以 10 后,2≡0.2(mod 3)显然不成立 —— 因为 10 和 3 不互质,除法运算破坏了同余关系。
这个性质告诉我们:在模运算中,加法、减法、乘法可以边计算边取模以防止溢出,但除法不能直接操作。而费马小定理的核心作用,就是为模运算中的除法提供一种 “转化方案”—— 将除法转化为乘法。
费马小定理(Fermat's Little Theorem)指出:
若 p 为质数,且整数 a 与 p互质(即gcd(a,p)=1),则有

。
费马小定理的最大作用,是为模运算中的除法提供 “乘法逆元”—— 这是解决模除法问题的关键。
对定理公式a^{p−1}≡1(mod p)进行变形:a×a^{p−2}≡1(mod p)
这个式子的意义是:在模 p 的意义下,a^{p−2}与 a 相乘的结果为 1。这恰好满足 “乘法逆元” 的定义 —— 我们称a^{p−2}是 a 模 p 的乘法逆元,记作a−1。
若 a 与 m 互质,且存在整数 x 使得a×x≡1(mod m),则 x 是 a 模 m 的乘法逆元。
有了逆元,模运算中的除法就可以转化为乘法:(a^b)mod p=(b×a−1)mod p
例如,计算(25÷5)mod3:
费马小定理的应用本质上是 “逆元的应用”,主要解决以下三类竞赛高频问题:
要应用费马小定理求逆元,必须高效计算a^{p−2} mod p—— 当 p 是 1e9+7 这样的大质数时,p−2可达 1e9 级别,直接循环计算会超时。此时需要 “快速幂算法”(Binary Exponentiation),将时间复杂度从O(n)降至O(logn)。
快速幂的本质是 “二进制分解”:将指数 b 分解为 2 的幂次之和,利用同乘性逐步计算幂次。例如,计算a10modp:
#include <iostream>
using namespace std;
typedef long long LL;
// 快速幂计算 (a^b) mod p
LL qpow(LL a, LL b, LL p) {
LL ret = 1; // 结果初始化为1
a = a % p; // 先对底数取模,防止溢出
while (b > 0) {
// 若当前二进制位为1,将结果乘上当前底数的幂次
if (b & 1) {
ret = ret * a % p; // 边乘边取模,避免溢出
}
// 底数平方,对应二进制位左移一位
a = a * a % p;
// 指数右移一位,处理下一个二进制位
b >>= 1;
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
LL a = 5, p = 3;
LL inv = qpow(a, p - 2, p); // 求5模3的逆元
cout << "5的逆元模3为:" << inv << endl; // 输出2
return 0;
}long long存储所有变量,且每次乘法后都取模,避免大数相乘导致的溢出;a*a不超过long long范围,若超过需用快速乘优化)。题目链接:https://www.luogu.com.cn/problem/P11465

题目描述:
核心思路:
#include <iostream>
using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7; // 质数,满足费马小定理条件
// 快速幂算法
LL qpow(LL a, LL b, LL p) {
LL ret = 1;
a %= p;
while (b) {
if (b & 1) ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
LL n, k;
cin >> n >> k;
if (k == 0) { // 特殊情况:k=0时,只能使用n次(每次消耗1张,无额外获得)
cout << n % MOD << endl;
continue;
}
// 等比数列求和:S = ( (k+1)^(n+1) - (k+1) ) / k
LL r = (k + 1) % MOD; // 公比
LL numerator = (qpow(r, n + 1, MOD) - r + MOD) % MOD; // 分子:(r^(n+1) - r),加MOD避免负数
LL inv_k = qpow(k % MOD, MOD - 2, MOD); // k的逆元
LL ans = numerator * inv_k % MOD;
cout << ans << endl;
}
return 0;
}示例输入:32 23 112 34
示例输出:1214178629506
题目链接:https://ac.nowcoder.com/acm/problem/15950

题目描述:
核心思路:
#include <iostream>
using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7;
// 快速幂求逆元
LL qpow(LL a, LL b, LL p) {
LL ret = 1;
a %= p;
while (b) {
if (b & 1) ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
LL n;
while (cin >> n) {
// 分别对n、n+1、2n+1取模,避免溢出
LL a = n % MOD;
LL b = (n + 1) % MOD;
LL c = (2 * n + 1) % MOD;
// 求6的逆元
LL inv6 = qpow(6, MOD - 2, MOD);
// 计算:(a*b % MOD) * c % MOD * inv6 % MOD
LL ans = a * b % MOD;
ans = ans * c % MOD;
ans = ans * inv6 % MOD;
cout << ans << endl;
}
return 0;
}示例输入:1 → 输出 1(1²=1)2 → 输出 5(1+4=5)1000 → 输出 333833500
ret = ret * a % p,确保中间结果始终在 p 范围内。a = a % p,确保底数始终在合理范围。费马小定理是模运算的核心工具,其本质是 “通过同余性质推导逆元,将除法转化为乘法”。 若想进一步深化学习,建议重点练习组合数取模、等比数列求和、大指数幂取模等题型,熟练掌握逆元的三种求法及其适用场景。如果在学习过程中遇到具体题目无法解决,或想了解欧拉定理、扩展欧几里得算法的深入应用,可以随时留言交流。后续将持续更新数论进阶内容,敬请关注!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=34m59s418000k