满足求余的运算
(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*x = 1 (mod p) 其中x叫做a关于p的逆元
比如2 * 3 % 5 = 1,(即2*3=1(mod 5))那么3就是2关于5的逆元,或者说2和3关于5互为逆元
a的逆元,我们用inv(a)来表示
那么(a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p
逆元:
a和p互质,a才有关于p的逆元
typedef long long ll; ll ex_gcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1,y=0; return a; } ll ans=ex_gcd(b,a%b,y,x); y-=a/b*x; return ans; } ll Cal(ll a,ll b,ll c)//求最小的x使ax+by=c { ll x,y; ll gcd=ex_gcd(a,b,x,y); if(c%gcd) return -1;//无解 x*=c/gcd; b/=gcd; if(b<0) b=-b; ll ans=x%b; if(ans<=0) ans+=b; return ans; } int Cal(int a,int b)//求最小的x使ax+by=1 { int x,y; int gcd=ex_gcd(a,b,x,y); if(1%gcd) return -1;//无解 x*=1/gcd; b=abs(b); int ans=x%b; if(ans<=0) ans+=b; return ans; }
求逆元方法1:
由费马小定理 a^(p-1) ≡1 (mod p)
两边同除以a得:a^(p-2) ≡ inv(a) (mod p)
所以inv(a) = a^(p-2) (mod p)
这个用快速幂可以求,复杂度O(logn)
LL quickPow(LL a,LL b,LL mod) //(a^b)%mod { LL res=1; while(b>0){ if(b&1) res=(res%mod*a%mod)%mod; b>>=1; a=(a%mod*a%mod)%mod; } return res; } LL Fermat(LL a, LL p) //费马求a关于b的逆元 { return quickPow(a,p-2,p); }
求逆元方法2:
扩展欧几里德算法
a*x + b*y = 1如果ab互质,有解。 这个解的x就是a关于b的逆元,y就是b关于a的逆元
证明:两边同时求余b
a*x % b + b*y % b = 1 % b
a*x % b = 1 % b
a*x = 1 (mod b)
所以x是a关于b的逆元,反之可证明y是b关于a的逆元
#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)); } }
求逆元方法3:
当p是质数的时候有inv(a) = (p - p / a) * inv(p % a) % p
#include<cstdio> typedef long long LL; LL inv(LL t, LL p) //求t关于p的逆元,注意:t要小于p,最好传参前先把t%p一下 { return t==1?1:(p-p/t)*inv(p%t,p)%p; } int main() { LL a,p; while(~scanf("%lld%lld",&a,&p)) { printf("%lld\n",inv(a%p,p)); } }
在O(n)的时间复杂度内算出n个数的逆元
#include<cstdio> const int N=200000+5; const int MOD=(int)1e9+7; int inv[N]; int init() { inv[1]=1; for(int i=2;i<N;i++) { inv[i]=(MOD-MOD/i)*1ll*inv[MOD%i]%MOD; } } int main() { init(); }