乘法逆元

在取模运算的除法取模那里,我们说取模除法并不能直接对除数取模。在离散数学中有群的概念,同余乘法构成一个群,我们在求除法取模时要把除以一个数当成乘以一个数的倒数,这个倒数在群论中被叫做逆元,因此我们要求的就是除数 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) ax1(mod p) ,且 a a a p p p互质,那么我们就能定义: x x x a a a p p p意义下的逆元,记为 a − 1 a^-1 a1 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) ax1(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) ap11(mod p)

由于证明过程较为繁琐,就不学证明了,直接运用:

a x ≡ 1 ( m o d   p ) ax ≡ 1 (mod ~p) ax1(mod p),再根据费马小定理得 a x ≡ a p − 1 ( m o d   p ) ax ≡ a^{p−1} (mod ~p) axap1(mod p)

所以 x ≡ a p − 2 ( m o d   p ) x ≡ a^{p−2} (mod~ p) xap2(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 1n在模 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 xi+y=p

然后 x ∗ i + y ≡ 0 ( m o d   p ) x*i+y ≡ 0 (mod ~p) xi+y0(mod p)

再有 y ≡ − x ∗ i ( m o d   p ) y ≡ -x*i (mod~ p) yxi(mod p)

同余式两边同除以 y ∗ i y*i yi y / ( y ∗ i ) ≡ − x ∗ i / ( y ∗ i ) ( m o d   p ) y/(y*i) ≡ -x*i/(y*i) (mod~ p) y/(yi)xi/(yi)(mod p)

将除法转化为乘法 i n v [ i ] ≡ − x ∗ i n v [ y ] ( m o d   p ) inv[i] ≡ -x*inv[y] (mod ~p) inv[i]xinv[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=pp/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](pp/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;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值