乘法逆元

luogu & cnblogs

逆元定义

如果 ax1(modp),则称 xa 在模 p 意义下的乘法逆元。

逆元存在当且仅当 ap,即 gcd(a,p)=1

ax1(modp) 转化可得 x1a(modp),那么模意义下 t÷a 就相当于 t×x

快速求单个数的逆元

快速幂

费马小定理

papP 时,有

ap11(modp)

由此转化可得 a×ap21(modp),此时就可以发现 ap2 即为 a 在模 p 意义下的乘法逆元。

此时可以用快速幂计算逆元,时间复杂度 O(logp)

扩展欧几里得算法

Exgcd 也可以用于求逆元。

由裴蜀定理可知,若 ap,则必然 y,使得 a×x+p×y=1 成立。

容易发现,a×x+p×y=1a×x1(modp)

使用 exgcd 解出这个方程即可,时间复杂度 O(logp)

线性求 1n 的逆元

例题:loj #110. 乘法逆元


现要求在 O(n) 的时间内,求出模 m 的意义下,[1,m) 中所有数的乘法逆元。

容易发现,mZ,有 1×11(modm),所以 1 的逆元恒为 1

考虑 递推

假设我们已知 [1,x) 内所有数的逆元,需要求出 x 的逆元。

将模数 m 表示为 kx+t,其中 k=mxt=mmodx

由此可得 kx+t0(modm)

两边同乘 x1×t1(modm),可得

kx×x1×t1+t×x1×t10(modm)k×t1+x10(modm)

即可得 x1k×t1(modm)

在此基础上代入 k=mxt=mmodx,可得

x1mx(mmodx)1(modm)

由于 (mmodx)1[1,x),所以 (mmodx)1 已知,线性递推即可。

  • PS 1:当 mmodx=0x 不存在模 m 意义下的乘法逆元。
  • PS 2:由于 C++ 中负数取模的结果为负数,所以在实际实现时递推式应写为 (m - m / x) * inv[m % x] % m

综上所述,递推式即为:

x1={1x=1mx(mmodx)1x1(modm)

线性递推即可在 O(n) 的时间内完成。

线性求任意 n 个数的逆元

例题:loj #161 乘法逆元 2


显然,对于每一个数用 快速幂 / exgcd 求解,时间复杂度为 O(nlogp),无法通过本题。

考虑使用前缀积 {sn},其中 si 记录 j=1iaj

此外需要前缀积的逆元,使用 {tn} 序列,其中 ti=si1(modp)

接下来可以通过 快速幂 / exgcd 求解 sn 的逆元,记为 tn

接下来可以通过如下公式线性递推求出 {tn}

tisi11j=1iajai+1j=1i+1ajai+1×si+11ai+1×ti+1(modp)

记原序列的逆元为 {invn},其中 invi=ai1(modp)

由于已知前缀积数组的逆元,那么

inviai11ai1sisi1si1sisi1×ti

故递推即可,时间复杂度为 O(n+logp),其中 O(logp) 是求 sn 逆元的复杂度。

posted @   OIer_wst  阅读(81)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
init();
点击右上角即可分享
微信分享提示