逆元和除法同余

逆元和除法同余

一、为什么需要逆元

在算法竞赛中,计算结果经常需要对一个大质数取模,比如 % 1000000007。

加、减、乘都可以直接对中间结果取模:

  • 加法:(a + b) % MOD
  • 减法:(a - b + MOD) % MOD
  • 乘法:(a * b) % MOD

但除法不行。

举个例子,计算 (10 / 5) % 3:

  • 正确结果:10 / 5 = 2,2 % 3 = 2
  • 如果先取模再除:10 % 3 = 1,5 % 3 = 2,1 / 2 = 0(整数除法),结果变成 0

结果对不上。原因是同余运算对除法不封闭。

那怎么办?这就需要乘法逆元。

二、逆元的定义

若整数 a 和 m 互质(gcd(a, m) = 1),且存在整数 x 使得:

a × x ≡ 1 (mod m)

则称 x 是 a 在模 m 意义下的乘法逆元,记作 a⁻¹。

通俗理解:逆元就是模意义下的倒数。

三、用逆元实现除法同余

如果你想计算 (a / b) % MOD,需要满足三个条件:

  1. a / b 必须能整除(每次除法都要保证绝对能整除)
  2. MOD 必须是质数(比如 1000000007,998244353)
  3. b 和 MOD 互质(即 gcd(b, MOD) = 1)

题目给定的数据一定会满足上面三点。满足后,有结论:

(a / b) % MOD = ((a % MOD) × (b 的逆元)) % MOD

也就是说:除法变成了乘以逆元。

而 b 的逆元怎么求?

b 的逆元 = b^(MOD-2) % MOD

这是基于费马小定理的结论,直接用快速幂计算即可。

四、模板代码


typedef long long ll;
const ll MOD = 1e9 + 7;

// 快速幂计算 (a^b) % MOD
ll qpow(ll a, ll b)
{
    ll res = 1;
    a %= MOD;
    while (b > 0)
    {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

// 求 b 的逆元(要求 MOD 是质数,且 b 与 MOD 互质)
ll inv(ll b)
{
    return qpow(b, MOD - 2);
}

// 计算 (a / b) % MOD
ll div_mod(ll a, ll b)
{
    return (a % MOD) * inv(b) % MOD;
}
int main()
{
    ll a = 10, b = 5;
    cout << div_mod(a, b) << endl;  // 输出 2
    return 0;
}

五、逆元的线性递推

(1) 连续数字逆元的线性递推

在 % p 意义下,
1、2、3 . . . n,求每个数的逆元

用 inv[i],代表 i 的逆元 (直接记住,不要问为什么)

inv[1] = 1;
inv[i] = (int)(p - (long) inv[p % i] * (p / i) % p);  // 从左往右线性递推

(2) 连续阶乘逆元的线性递推

在 % p 意义下,
1!、2!、3! . . . n!,求每个数的逆元,

用 inv[i],代表 i! 的逆元(比较简单,容易理解)
inv[i] 相当于 等价于1 / i !, 那么int[i - 1] 等价于 1 / (i - 1)!, 不就是inv[i] * (i) % MOD
做法:先求 n! 乘法同余的结果,假设为 a,然后求 a 的逆元,假设为 b

inv[n] = b;
inv[i] = ((long) (i + 1) * inv[i + 1]) % MOD;  // 从右往左线性递推

*六、扩展:扩展欧几里得求逆元

当 MOD 不是质数时,用扩展欧几里得求逆元:

// 扩展欧几里得:求 ax + by = gcd(a, b) 的一组解
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if (b == 0)
    {
        x = 1; y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

// 求 a 在模 m 意义下的逆元(要求 gcd(a, m) = 1)
ll inv_exgcd(ll a, ll m)
{
    ll x, y;
    ll d = exgcd(a, m, x, y);
    if (d != 1) return -1;  // 逆元不存在
    return (x % m + m) % m;
}

七、总结

逆元就是模意义下的倒数。

计算 (a / b) % MOD:

  1. 先求 b^(MOD-2) % MOD(b 的逆元)
  2. 再算 (a % MOD) * inv(b) % MOD

左神特别强调:这部分记住用法和模板即可,证明过程没有太多扩展性,当做原子技能使用。

posted @ 2026-10-01 01:18  leaderling  阅读(2)  评论(0)    收藏  举报