[数论]-----乘法逆元

乘法逆元介绍

如果 a x ≡ 1 ( m o d p ) a x \equiv 1 (mod p) ax1(modp),且 GCD(a,p) = 1,则称 a 的模p意义下的乘法逆元为 x。
同时 a 也是 x 模 p 意义下的乘法逆元。a 的逆元记为 a − 1 a^{-1} a1

乘法逆元的作用

首先,除法取模不像乘法取模那样 “自由”:(a / b)%p ≠ \neq = (a%p / b%p)%p,所以如果你想求一个大分式取模,即 a 和 b 都是结果很大的表达式,计算会十分复杂。所以引入了乘法逆元,将除法取模转换为乘法取模来简化计算。
结论:(a/b)%p = (a × b − 1 \times b^{-1} ×b1) % p。
当然 b 和 p 要互质,这样才有逆元。

求乘法逆元的主要方法

方法一:扩展欧几里得算法。
如果 ax ≡ \equiv 1 (mod p),那么 ax % p = 1。那么存在 x,y 使得 ax + py = 1,直接用扩展欧几里得算法求出 x 和 y,x 就是 a 在模 p 意义下的逆元。
代码:

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

方法二:费马小定理+快速幂。
费马小定理内容:如果 p 是一个质数,而整数 a 不是 p 的倍数,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1(modp) ap11(modp)
a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1(modp) ap11(modp) 很容易得到 a × a p − 2 ≡ 1 ( m o d p ) a\times a^{p-2} \equiv 1(modp) a×ap21(modp),当 a,p 互质时, a p − 2 a^{p-2} ap2 就是 a 在模p 意义下的逆元。但前提是 p 是一个质数,这样才有费马小定理成立。然后用快速幂算法求 a p − 2 a^{p-2} ap2 即可。

代码略。

线性求1~n的逆元

求出1,2,3…n 中每个数字关于 p 的逆元,如果对每个数字使用上面的两种方法,速度就很慢了,但我们可以推导出逆元的线性递推式。
首先有: 1 − 1 ≡ 1 1^{-1} \equiv 1 111 (mod p) ,
然后设: p = k i + j p = ki+j p=ki+j ( j < i , 1 < i < p ) (j<i,1<i<p) (j<i1<i<p),再放到 mod p 意义下就会得到: k i + j ≡ 0 ki+j \equiv 0 ki+j0 (mod p),
两边同时乘 i − 1 , j − 1 i^{-1},j^{-1} i1j1
k j − 1 + i − 1 ≡ 0 kj^{-1}+i^{-1} \equiv 0 kj1+i10 (mod p),
i − 1 ≡ − k j − 1 i^{-1} \equiv -kj^{-1} i1kj1 (mod p),
i − 1 ≡ − ( p i ) ( p m o d i ) − 1 i^{-1} \equiv -(\frac {p}{i})(p mod i^{})^{-1} i1(ip)(pmodi)1
然后我们就可以推出逆元了。
代码:

inv[1] = 1;
for (int i = 2; i <= n; ++i) 
    inv[i] = (long long)(p - p / i) * inv[p % i] % p;

线性求任意n个数的逆元

上面的方法只能求 1~n 的逆元,如果需要求任意给定 n 个数 ( 1 ≤ a i < p ) (1 \leq a_i < p) (1ai<p)的逆元,就需要用下面的方法。
首先计算 n n n 个数的前缀积,记为 s i s_i si,然后使用快速幂或扩展欧几里得算法计算 s n s_n sn 的逆元,记为 s v n sv_n svn
因为 s v n sv_n svn n n n 个数的积的逆元,所以当我们把它乘上 a n a_n an 时,就会和 a n a_n an 的逆元抵消,于是就得到了 a 1 a_1 a1 a n − 1 a_{n-1} an1 的积逆元,记为 s v n − 1 sv_{n-1} svn1
同理我们可以依次计算出所有的 s v i sv_i svi,于是 a i − 1 a_i^{-1} ai1 就可以用 s i − 1 × s v i s_{i-1} \times sv_i si1×svi 求得。
所以我们就在 O ( n + l o g p ) O(n + logp) O(n+logp) 的时间内计算出了 n n n 个数的逆元。
代码:

s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = qpow(s[n], p - 2);
// 当然这里也可以用 exgcd 来求逆元,视个人喜好而定.
for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p;
for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p;

此部分来自 OI Wiki.

例题

求逆元模板: 洛谷P1082 同余方程.

逆元简单应用:洛谷P2613 【模板】有理数取余.

线性逆元模板:洛谷P3811 【模板】乘法逆元.

任意N个数字逆元:洛谷P5431 【模板】乘法逆元2.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值