火龙信奥
  • 首页
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

乘法逆元:把取模中的“除法”变成“乘法”

作者: 作者的头像   刘杰 , 时间:2026-05-29 22:50:02 , 所有人可见, 阅读  121

乘法逆元:把取模中的“除法”变成“乘法”

在 OI 里,很多题都会要求答案对一个数取模,比如:

$ans \bmod 998244353$

或者:

$ans \bmod 1000000007$

这时如果式子里有除法,比如:

$\frac{a}{b} \bmod MOD$

我们通常不能直接写:

ans = a / b % MOD;

因为取模之后,普通除法经常会失效。

正确思路是:

  • 不直接除以 $b$
  • 而是乘上 $b$ 在模 $MOD$ 意义下的乘法逆元

也就是把:

$\frac{a}{b}$

改成:

$a \times b^{-1}$

其中 $b^{-1}$ 满足:

$b \times b^{-1} \equiv 1 \pmod {MOD}$

这个 $b^{-1}$ 就叫做 $b$ 在模 $MOD$ 意义下的 乘法逆元。


为什么取模下不能随便除

在普通整数里:

$6 \div 3 = 2$

当然没问题;但在取模的操作里,我们关心的是“余数”。比如模 $5$ 操作有:

$6 \equiv 1 \pmod 5$

如果你先除再取模:

$6 \div 3 = 2$

得到的是 $2$。但如果你先把 $6$ 变成余数:

$6 \equiv 1 \pmod 5$

那问题就变成:

$1 \div 3 \pmod 5$

这时“除以 $3$”到底是什么意思?

在模意义下,我们不把它理解成普通除法,而是理解成:

乘上一个数 $x$,让 $3x$ 的余数等于 $1$。

也就是找:

$3x \equiv 1 \pmod 5$

试一下可以发现:

$3 \times 2 = 6 \equiv 1 \pmod 5$

所以:

$3^{-1} \equiv 2 \pmod 5$

于是:

$1 \div 3 \equiv 1 \times 2 \equiv 2 \pmod 5$

这次结果刚好还是 $2$,但关键是:取模意义下的除法,本质上是乘逆元。


乘法逆元

如果有两个整数 $a$ 和 $x$,满足:

$a \times x \equiv 1 \pmod m$

那么 $x$ 就叫做 $a$ 在模 $m$ 意义下的乘法逆元。

通常写成:

$x \equiv a^{-1} \pmod m$

也就是:

$a \times a^{-1} \equiv 1 \pmod m$

也就是说:

  • 普通数学里,$a$ 的倒数是 $\frac{1}{a}$
  • 取模数学里,$a$ 的“倒数”就是 $a^{-1}$
  • 普通除法里的除以 $a$,在取模里就变成乘 $a^{-1}$

所以:

$\frac{b}{a} \equiv b \times a^{-1} \pmod m$


逆元的存在性

逆元不一定存在。我们说 $a$ 在模 $m$ 意义下有逆元,当且仅当:

$\gcd(a,m)=1$

也就是说,$a$ 和 $m$ 必须互质。

比如说 $3$ 和 $5$ 互质,所以 $3$ 在模 $5$ 意义下有逆元;这是因为:

$3 \times 2 \equiv 1 \pmod 5$

所以:

$3^{-1} \equiv 2 \pmod 5$

再举个没有逆元的例子:

$2$ 和 $6$ 不互质,因为:

$\gcd(2,6)=2$

我们想找:

$2x \equiv 1 \pmod 6$

但 $2x$ 一定是偶数,它对 $6$ 取模后只可能是:$0,2,4$,不可能得到 $1$。

所以 $2$ 在模 $6$ 意义下没有逆元。


为什么算法竞赛里经常能用逆元

因为很多题的模数是质数,比如:

  • $998244353$
  • $1000000007$
  • $1000000009$

如果 $MOD$ 是质数,那么只要 $a$ 不是 $MOD$ 的倍数,就一定有:

$\gcd(a,MOD)=1$

也就是说,大多数时候我们要求的分母都有逆元。

于是组合数、概率、期望、DP 计数里的除法,都可以写成乘逆元。

例如:

$\binom{n}{k}=\frac{n!}{k!(n-k)!}$

在模 $MOD$ 意义下可以写成:

$\binom{n}{k}\equiv n! \times (k!)^{-1} \times ((n-k)!)^{-1} \pmod {MOD}$

这就是组合数预处理里经常出现 fac 和 ifac 的原因。


方法一:快速幂求逆元

这是最常用的方法。

前提:

  • $MOD$ 是质数
  • $a$ 不是 $MOD$ 的倍数

根据费马小定理:

$a^{MOD-1}\equiv 1 \pmod {MOD}$

把左边拆开看:

$a^{MOD-1}=a\times a^{MOD-2}$

所以:

$a^{MOD-2}\equiv a^{-1} \pmod {MOD}$

也可以写成:

$a^{-1}\equiv a^{MOD-2} \pmod {MOD}$

也就是说,只要求:

$a^{MOD-2} \bmod MOD$

就能得到 $a$ 的逆元。

快速幂代码如下:

using ll = long long;

ll qpow(ll a, ll b, ll mod) {
    ll res = 1;
    a %= mod;
    while (b > 0) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

ll inv(ll a, ll mod) {
    return qpow(a, mod - 2, mod);
}

使用例子:

#include <bits/stdc++.h>
using namespace std;

const int MOD = 998244353;

int main() {
    long long a = 3;
    long long inv_a = inv(a, MOD);
    cout << inv_a << '\n';
    return 0;
}

复杂度:

$O(\log MOD)$

这种写法简单、稳定,比赛里常用。


方法二:扩展欧几里得求逆元

上面的快速幂求逆元法依赖 $MOD$ 是质数。如果 $MOD$ 不一定是质数,但 $a$ 和 $MOD$ 互质,也可以用扩展欧几里得算法。

因为当:

$\gcd(a,m)=1$

根据裴蜀定理,一定存在整数 $x$ 和 $y$,使得:

$ax+my=1$

两边对 $m$ 取模:

$ax+my\equiv 1 \pmod m$

因为:

$my\equiv 0 \pmod m$

所以:

$ax\equiv 1 \pmod m$

这说明 $x$ 就是 $a$ 在模 $m$ 意义下的逆元。

代码如下:

using ll = long long;

ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll x1, y1;
    ll g = exgcd(b, a % b, x1, y1);
    x = y1;
    y = x1 - a / b * y1;
    return g;
}

ll inv(ll a, ll mod) {
    ll x, y;
    ll g = exgcd(a, mod, x, y);
    if (g != 1) return -1; // 不存在逆元
    x %= mod;
    if (x < 0) x += mod;
    return x;
}

复杂度:

$O(\log MOD)$

这个方法的优点是:

  • 模数不要求是质数
  • 只要 $a$ 和 $MOD$ 互质,就能求

方法三:线性递推求一整段逆元

有时我们不是只求一个数的逆元,而是要求:

$1^{-1},2^{-1},3^{-1},\dots,n^{-1}$

如果 $MOD$ 是质数,并且 $1 \le i < MOD$,可以用线性递推:

$inv[1]=1$

$inv[i]=(MOD-\lfloor\frac{MOD}{i}\rfloor)\times inv[MOD\bmod i]\bmod MOD$

代码如下:

const int N = 1000000;
const int MOD = 998244353;

long long inv[N + 5];

void init_inv() {
    inv[1] = 1;
    for (int i = 2; i <= N; ++i) {
        inv[i] = 1LL * (MOD - MOD / i) * inv[MOD % i] % MOD;
    }
}

复杂度:$O(n)$

它适合一次性预处理很多个逆元。


阶乘逆元与组合数

组合数是逆元最常见的应用之一。

我们知道:

$\binom{n}{k}=\frac{n!}{k!(n-k)!}$

在模意义下:

$\binom{n}{k}\equiv fac[n]\times ifac[k]\times ifac[n-k]\pmod {MOD}$

其中:

  • $fac[i]$ 表示 $i!$
  • $ifac[i]$ 表示 $(i!)^{-1}$

预处理方式如下:

const int N = 1000000;
const int MOD = 998244353;

long long fac[N + 5], ifac[N + 5];

long long qpow(long long a, long long b) {
    long long res = 1;
    while (b > 0) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

void init_comb() {
    fac[0] = 1;
    for (int i = 1; i <= N; ++i) {
        fac[i] = fac[i - 1] * i % MOD;
    }

    ifac[N] = qpow(fac[N], MOD - 2);
    for (int i = N; i >= 1; --i) {
        ifac[i - 1] = ifac[i] * i % MOD;
    }
}

long long C(int n, int k) {
    if (k < 0 || k > n) return 0;
    return fac[n] * ifac[k] % MOD * ifac[n - k] % MOD;
}

注意这个模板默认 $MOD$ 是质数,并且通常要求预处理范围里的阶乘不要变成 $0$,也就是常见场景下有 $N

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

©2026加盟我们 | 关于我们 | ACM课程 | 常见问题 | 成果墙 | 评测记录 | 浙ICP备2021013995号
在线画图 | OI WIki | 打字练习
火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码