为什么会有逆元这个概念。
因为 有时我们需要计算 (A/B) % M 的值, 如果B过大,或者A过大,可能会爆精度,然后我们就想到能不(A%M / B%M)
发现这种方法不对,那我们可以将除法换成乘法(A * )% M 这样就能转化成 (A % M * % M) % M
那我们如何来求 呢?
在就是B 的倒数,但是我们求 虽然不完全是倒数,是逆元 (倒数的扩展)
B的倒数就 是 B 对 M 取模 等于 1 。
然后我们推一下 (a/b)%m
设c是b的逆元,则有b*c≡1(mod m);
则(a/b)%m = (a/b)*1%m = (a/b)*(b*c%m)%m= a*c(mod m);
即a/b的模等于a*b的逆元的模;
求逆元
求一个数的逆元(欧几里得,a 和 m 互质)
求多个数的逆元(线性筛选)
求一个数的逆元也可以用费马小定理(时间线性且 m为质数)
逆元求法1
通过扩展欧几里得 来求
限制 a 与 m 互质
时间复杂度 O(logn)。
原理: 求 a 关于 m 的逆元
a * x = 1( mod m)
可以化简为
a * x = m * y + 1
a * x - m * y = 1
是不是一个二元方程求解 ? 直接上扩展欧几里得求解
ll ex_gcd(ll a, ll b, ll &x, ll &y){
if(!b){
x = 1;
y = 0;
return a;
}
ll d = ex_gcd(b, a%b, x, y);
ll t = x;
x = y;
y = t - (a/b) * y;
return d;
}
ll inv(ll a, ll m){
ll x, y;
ll d = ex_gcd(a, m, x, y);
x = (x % m + m) % m;
return x;
}
逆元求法2
费马小定理
有数据范围限制 因为用到快速幂
限制 1 <= x <= 1e9 而且 m 为质数
就是 a 的逆元
时间复杂度 O(logn)
ll quick_pow(ll a, ll b){
if(b < 0) return 0;
ll ret = 1;
a %= m;
while(b){
if( b & 1) ret = (ret * a) % m;
b >>= 1;
a = (a * a) % m;
}
return ret;
}
ll inv(ll a){
return quick_pow(a, m - 2);
}
逆元求法 3
逆元线性筛选
限制 : 求1,2,...,N关于 M 的逆元(M为质数)
时间复杂度 O (N)
const int mod = 1000000009;
const int maxn = 10005;
int inv[maxn];
inv[1] = 1;
for(int i = 2; i < 10000; i++)
inv[i] = inv[mod % i] * (mod - mod / i) % mod;
逆元求法4
求阶乘的逆元
inv[maxn]=mod_pow(fac[maxn],mod-2); // fac[] 是阶乘数组
for(ll i=maxn-1;i>=0;i--)
inv[i]=(inv[i+1]*(i+1))%mod;