若a*x≡1(mod b) ,a,b互质,则称x为a的逆元。
A/B %p=A*inv(B) %p inv(B)为B的逆元
(a/b)%m 时,因b可能会过大,会出现爆精度的情况,且除法不满足同余拆分定理。
(1).费马小定理
在m是素数的情况下,对任意整数都有。
如果无法被整除,则有。
可以在为素数的情况下求出一个数的逆元,,即为逆元。
假设数据范围1<=x<=10^9,p=1000000007,p是素数;
所以x肯定就无法被p整除啊,所以最后就得出x^(p-2)为x的逆元啦。
复杂度O(logn);
代码:
const int mod = 1000000009;
long long quickpow(long long a, long long b) {
if (b < 0) return 0;
long long ret = 1;
a %= mod;
while(b) {
if (b & 1) ret = (ret * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return ret;
}
long long inv(long long a) {
return quickpow(a, mod - 2);
}
(2)扩展欧几里得算法求逆元
例如:4关于1模7的乘法逆元为多少?
4X≡1 mod 7
这个方程等价于求一个X和K,满足
4X=7K+1
其中X和K都是整数。
求x,k就是扩展欧几里得算法
可扩展欧几里得求逆元ax≡1(mod n)其中a,n互质;
根据逆元的定义,则可以转化为a*x+b*y=1。这样就可以用扩展欧几里得算法求x了。
注意:在gcd不为1说明逆元不存在(因为c=1,c%gcd==0为有整数解的充分必要条件),若为1,调整x0到0~m-1的范围中即可
适用条件:
复杂度:O(logn);
代码:
//返回d = gcd(a, b);和对应于等式ax + by = d中的x、y
int ex_gcd(int a,int b,int &x,int &y) //扩展欧几里得
{
if(b==0)
{
x=1;
y=0;
return a;
}
int r=ex_gcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return r;
}
int mod_reverse(int a,int b)//a*x=1(mod b) 求a的逆元x
{
int d,x,y;
d=ex_gcd(a,b,x,y);
if(d==1)
return (x%b+b)%b;
else
return -1;
}
* 只能求a < m的情况,且a与m互质
* 求ax = 1(mod m)的x值,即逆元(0 < a < m)
long long inv(long long a,long long m){
if(a == 1)return 1;
return inv(m%a,m)*(m-m/a)%m;
}
//x[i]=temp/a[i][i]%mod
x[i] = (temp*inv(a[i][i],mod))%mod;
(3) 逆元线性筛 ( P为质数 )
求1,2,...,N关于P的逆元(P为质数)
复杂度: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;
如果是求阶乘的逆元呢?(阶乘数组:fac[ ])
代码:
inv[maxn]=mod_pow(fac[maxn],mod-2);
for(ll i=maxn-1;i>=0;i--)
inv[i]=(inv[i+1]*(i+1))%mod;
(4)
但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求与互素。实际上我们还有一
种通用的求逆元方法,适合所有情况。公式如下
现在我们来证明它,已知,证明步骤如下
方法八 、递推O(1)就能求出每一个的逆元
#define MOD 1000000007
#define N 500005
LL inv[N],c[N],a[N];
//预处理逆元 i (i