在分数取模时总会用到逆元运算
一般只需要求出分母关于取模的逆元即可
然后分子乘以该逆元就是分数取模的结果
在模运算中
(a + b) % p = (a%p + b%p) %p (对)
(a - b) % p = (a%p - b%p) %p (对)
(a * b) % p = (a%p * b%p) %p (对)
(a / b) % p = (a%p / b%p) %p (错)
对于除法的模运算不适合,所以需要定义一个新的东西来求除法模运算,这个东西就是逆元
定义:
(a/b)(mod p)=(ainv[b])(mod p)或者
ab≡1(mod P)`
inv[b]就是b的逆元 a就是b的逆元,或者ab或为逆元
则根据定义可知
除以一个数再取模等同于乘以这个数的逆元再取模
但是我们不能直接说一个数的逆元是多少,
应该这么说: 一个数 x 在模 p 的条件下的逆元是多少
比如2 * 3 % 5 = 1,那么3就是2关于5的逆元,或者说2和3关于5互为逆元
(a*inv[b])%p,inv[b]就是a关于p的逆元
条件:a与p互为质数,a才有关于p的逆元
转换除法模运算
根据取模运算
(a + b) % p = (a%p + b%p) %p (对)
(a - b) % p = (a%p - b%p) %p (对)
(a * b) % p = (a%p * b%p) %p (对)
(a / b) % p = (a%p / b%p) %p (错)
(a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p
求逆元
方法1--费马小定理:
如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。
a^(p-1) ≡1 (mod p)
两边同除以a
a^(p-2) ≡1/a (mod p)
a^(p-2) ≡ inv(a) (mod p)
inv(a) = a^(p-2)
即可求出a的逆元时a^(p-2)
用快速幂求,复杂度O(logn)
LL pow_mod(LL a, LL b, LL p){//a的b次方求余p
LL ret = 1;
while(b){
if(b & 1) ret = (ret * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
LL Fermat(LL a, LL p){//费马求a关于b的逆元
return pow_mod(a, p-2, p);
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a, p));
}
}
方法2--扩展欧几里得
ax + by = 1
如果ab互质,有解
这个解的x就是a关于b的逆元
y就是b关于a的逆元
两边同时求余b
ax % b + by % b = 1 % b
ax % b = 1 % b
ax = 1 (mod b)
反之可证明y
用扩展欧几里得模板求
#include<cstdio>
typedef long long LL;
void ex_gcd(LL a,LL b,LL &x,LL &y,LL &d){
if(!b){
d=a,x=1,y=0;
}else{
ex_gcd(b,a%b,y,x,d);
y-=x*(a/b);
}
}
LL inv(LL t, LL p){//如果不存在,返回-1
LL d, x, y;
ex_gcd(t, p, x, y, d);
return d==1 ? (x%p+p)%p : -1;
}
int main(){
LL a, p;
while(~scanf("%lld%lld", &a, &p)){
printf("%lld\n", inv(a, p));
}
}
例题:
hdu1576
求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。
因为9973为质数,作为p
本题要求b的逆元,设c为b的逆元
(A/B)mod P = (A/B)1mod p = (A/B)BC mod p=AC(mod p);
利用费马小定理得到C=B^(p-2)
答案就是(AC)%p=(A%pC%p)%p
所以先利用费马小定理得到逆元,利用快速幂
再代入等式求出答案