记录自己学的逆元求法
自己粗略的对逆元理解:
逆元 :关于 a/b对于mod的取模,因为 (a%mod) / (b%mod) != (a/b)%mod; 就是说在除法当中执行取模操作对最后答案的贡献是不正确的,所以我们要引用逆元知识点 ,将除法转换为乘法进行取模 (a%mod * b%mod == (a*b)%mod ) (取模操作 加减乘 都是可以,唯独除法不行 (除法太卑微了) - - )
所以,对于 (a/b) %mod 转换为 a * b^(-1) 次方 ,而 b^(-1) 则可以用逆元求出
逆元的三种求法
- 扩展欧几里得求逆元
- 费马小定理求逆元
- 线性递推求逆元
扩展欧几里得
//扩展欧几里得求逆元 模数为质数
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x = 1;
y=0;
return a;
}
ll d = exgcd(b,a%b,y,x);
y -= a/b*x;
return d;
}
ll inv(ll a,ll mod){
ll x,y;
ll d =exgcd(a,mod,x,y);
if(d == 1){
return (x%mod+mod)%mod;
}
return -1;
}
int main(void){
/* ----- 初始内容省略 */
/* 调用方式 */
ll ans = inv(b,mod); // 此时的b就是 a/b 中的 b,然后利用扩展欧几里得 求出 关于b的逆元
return 0;
}
费马小定理
相对而言,费马小定理就简单多了 (费马小定理跟快速幂更配噢)
//快速幂
ll pow2(ll a,ll n){
ll res = 1;
while(n){
if(n&1) res = (res*a)%mod;
a = (a*a)%mod;
n >>= 1;
}
return res%mod;
}
int main(void){
/* 利用快速幂求关于b的逆元 */
/* a^(p-1) = 1 (mod p) p 为模数且是质数*/
/* a^(p-2) = a^-1 (mod p) a^(mod-2) 即为 a 的逆元*/
ll ans = (pow2(b,mod-2))%mod;
return 0;
}
线性递推
其实就类似一个打表,通常情况下 mod 模数不是很大没有超过1e7左右这样子
ll f2[mod+5];
void init(){
f2[1] = 1; //初始化边界 为1
for(int i = 2;i<mod+5;++i){
f2[i] = (mod-mod/i)*f2[mod%i] %mod; //线性递推
/* 证明: */
/*
M 即为模数(mod)
设t=M/i,k=M%i,那么
t*i+k≡0(Mod M)
-t*i≡k(Mod M)
对上式两边同时除 i×k,进一步得到
-t*inv[k]≡inv[i](Mod M)
再把和替换掉,最终得到
inv[i]=(M-M/i)*inv[M%i]%M
(直接搬过来了)
*/
}
}
int main(void){
ll ans = f2[b]; // 返回的是关于b的逆元
return 0;
}