在取模运算的除法取模那里,我们说取模除法并不能直接对除数取模。在离散数学中有群的概念,同余乘法构成一个群,我们在求除法取模时要把除以一个数当成乘以一个数的倒数,这个倒数在群论中被叫做逆元,因此我们要求的就是除数 x x x的乘法逆元 i n v ( x ) inv(x) inv(x)。
乘法逆元
已知 a / b ( m o d p ) a/b (mod ~p) a/b(mod p), p p p为质数
若 a x ≡ 1 ( m o d p ) ax ≡ 1 (mod~ p) ax≡1(mod p) ,且 a a a与 p p p互质,那么我们就能定义: x x x为 a a a模 p p p意义下的逆元,记为 a − 1 a^-1 a−1或 i n v ( a ) inv(a) inv(a)。
下面简称乘法逆元为逆元
扩展欧几里得求逆元
此方法的优点是 p p p不是质数但满足 a a a和 p p p互质时也可以使用
由 a x ≡ 1 ( m o d p ) ax ≡ 1 (mod~ p) ax≡1(mod p),我们可以转化为求解 a x + p y = 1 ax+py=1 ax+py=1,再根据扩展欧几里得算法即可求解 x x x
由于最后的求得的 x x x可能是负数,因此我们再进行处理: ( x % p + p ) % p (x\%p+p)\%p (x%p+p)%p
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x=1;y=0;
return a;
}
ll gcd=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return gcd;
}
ll inv(ll a,ll p){
ll x,y;
exgcd(a,p,x,y);
return (x%p+p)%p;
}
快速幂取模求逆元
费马小定理
若 p p p为素数, a a a为正整数,且 a , p a,p a,p互质。 则有 a p − 1 ≡ 1 ( m o d p ) a^{p−1} ≡ 1 (mod ~p) ap−1≡1(mod p)
由于证明过程较为繁琐,就不学证明了,直接运用:
由 a x ≡ 1 ( m o d p ) ax ≡ 1 (mod ~p) ax≡1(mod p),再根据费马小定理得 a x ≡ a p − 1 ( m o d p ) ax ≡ a^{p−1} (mod ~p) ax≡ap−1(mod p)
所以 x ≡ a p − 2 ( m o d p ) x ≡ a^{p−2} (mod~ p) x≡ap−2(mod p)
这样我们直接使用快速幂取模就可以求出逆元:
ll quick_mod(ll x,ll n,ll p){
ll ans=1;
while(n){
if(n&1) ans=ans*x%p;
x=x*x%p;
n>>=1;
}
return ans;
}
ll inv(ll a){
return quick_mod(a,p-2,p);
}
线性求逆元
1.求 1 − n 1 - n 1−n在模 p p p下的所有乘法逆元( n < p n<p n<p且 p p p为质数)
如果一个个求,显然时间复杂度有点高,因此我们试着找下递推的规律
令 x = p / i x=p/i x=p/i , y = p % i y=p\%i y=p%i, 则有: x ∗ i + y = p x*i+y=p x∗i+y=p
然后 x ∗ i + y ≡ 0 ( m o d p ) x*i+y ≡ 0 (mod ~p) x∗i+y≡0(mod p)
再有 y ≡ − x ∗ i ( m o d p ) y ≡ -x*i (mod~ p) y≡−x∗i(mod p)
同余式两边同除以 y ∗ i y*i y∗i 得 y / ( y ∗ i ) ≡ − x ∗ i / ( y ∗ i ) ( m o d p ) y/(y*i) ≡ -x*i/(y*i) (mod~ p) y/(y∗i)≡−x∗i/(y∗i)(mod p)
将除法转化为乘法 i n v [ i ] ≡ − x ∗ i n v [ y ] ( m o d p ) inv[i] ≡ -x*inv[y] (mod ~p) inv[i]≡−x∗inv[y](mod p)
最后将 x = p / i , y = p % i = p − p / i x=p/i , y=p\%i=p-p/i x=p/i,y=p%i=p−p/i带入就可以得到:
递推公式 i n v [ i ] ≡ ( p − p / i ) ∗ i n v [ p % i ] ( m o d p ) inv[i] ≡ (p-p/i)*inv[p\%i] (mod~ p) inv[i]≡(p−p/i)∗inv[p%i](mod p)
const int N=?;
int inv[N]
void solve(int n,int p){
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=(p-p/i)*inv[p%i]%p;
}
2.求阶乘 1 ! − n ! 1! - n! 1!−n!的逆元
设 f a c fac fac数组保存的是 i i i的阶乘,类似上面的推导,我们有:
f a c [ i ] ∗ i n v [ i ] ≡ 1 ( m o d p ) fac[i]*inv[i] ≡ 1 (mod~ p) fac[i]∗inv[i]≡1(mod p)
由 f a c [ i + 1 ] ∗ i n v [ i + 1 ] ≡ 1 ( m o d p ) fac[i+1]*inv[i+1] ≡ 1 (mod ~p) fac[i+1]∗inv[i+1]≡1(mod p) 得 f a c [ i ] ∗ ( i + 1 ) ∗ i n v [ i + 1 ] ≡ 1 ( m o d p ) fac[i]*(i+1)*inv[i+1] ≡ 1 (mod~ p) fac[i]∗(i+1)∗inv[i+1]≡1(mod p)
由同余的除法可得 : i n v [ i ] ≡ i n v [ i + 1 ] ∗ ( i + 1 ) inv[i] ≡ inv[i+1] * (i+1) inv[i]≡inv[i+1]∗(i+1)
故我们先由快速幂取模求出 i n v [ n ! ] inv[n!] inv[n!]再往下递推求出所有的逆元
ll fac[N];
int inv[maxn];
ll p;
ll quick_mod(ll x,ll n,ll p){
ll ans=1;
while(n){
if(n&1) ans=ans*x%p;
x=x*x%p;
n>>=1;
}
return ans;
}
void solve(int n){ //求1!-n!的所有逆元
fact[0]=1;
for (int i=1;i<=n; i++) {
fact[i]=fact[i-1]*i%p;
}
inv[n]=quick_mod(fact[n],p-2,p);
for (int i=n-1;i>=0;i--) {
inv[i]=inv[i+1]*(i+1)%mod;
}
}