今天我们聊一聊逆元这个东西。
那么逆元是个什么东西呢?我们可以发现(a/b)%p=((a%p)/(b%p))%p是错的。(显然)
但如果我们一定要求(a/b)%p怎么办呢?这时就需要逆元了。
我们引入逆元的概念。
a*x=1(mod p),我们就把x看成a的倒数,只不过加了一个求余条件,所以x叫做a关于p的逆→→→→→→→→元。
a的逆元,我们用inv(a)来表示,那么(a/b)%p=(a*inv(b))%p=(a%p*inv(b)%p)%p(a与p互质)。这样就把除法,完全转换为乘法了。
那么逆元怎么求呢?我们来看3种方法。
一、费马小定理
a^(p-1)≡1(mod p)两边同除以a→a^(p-2)≡inv(a)(mod p)
所以inv(a)=a^(p-2)(mod p)
这个用快速幂求一下,复杂度O(logn)
Code:
#include<bits/stdc++.h> #define ll long long using namespace std; ll power(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1)ans=(ans*a)%p; a=(a*a)%p; b>>=1; } return ans; } ll Fermat(ll n,ll p) { return power(n,p-2,p); } int main() { ll n,p; scanf("%lld%lld",&n,&p); printf("%lld\n",Fermat(n,p)); return 0; }
方法二:
要用扩展欧几里德算法
对于a*x+b*y=1如果ab互质,则有解,这个解的x就是a关于b的逆元,y就是b关于a的逆元。Why?
(a*x)%b+(b*y)%b=1%b
(a*x)%b=1%b
a*x=1(mod b)
所以x是a关于b的逆元,反之可证明y。
Code:
#include<bits/stdc++.h> #define ll long long using namespace std; ll exgcd(ll a,ll b,ll &x,ll &y,ll &d) { if(b==0)x=1,y=0,d=a;else { exgcd(b,a%b,y,x,d); y-=x*(a/b); } } ll inv(ll n,ll p) { ll x,y,d; exgcd(n,p,x,y,d); return d==1?(x%p+p)%p:-1; } int main() { ll n,p; scanf("%lld%lld",&n,&p); printf("%lld\n",inv(n,p)); return 0; }
方法三:
当p是个质数的时候有
inv(a)=(p-p/a)*inv(p%a)%p
证明:
设x=p%a,y=p/a
于是有x+y*a=p(x+y*a)%p=0
移项得x%p=(-y)*a%p
x*inv(a)%p=(-y)%p
inv(a)=(p-y)*inv(x)%p
于是inv(a)=(p-p/a)*inv(p%a)%p
然后一直递归到1为止,因为1的逆元就是1
Code:#include<bits/stdc++.h> #define ll long long using namespace std; ll inv(ll n,ll p) { return n==1?1:(p-p/n)*inv(p%n,p)%p; } int main() { ll n,p; scanf("%lld%lld",&n,&p); printf("%lld\n",inv(n,p)); return 0; }
这个方法不限于求单个逆元,比前两个好,它可以在O(n)的复杂度内算出n个数的逆元
Code:#include<bits/stdc++.h> #define ll long long #define N 3000005 using namespace std; ll inv[N]; int main() { ll n,p; scanf("%lld%lld",&n,&p); inv[1]=1;puts("1"); for(int i=2;i<=n;i++) { inv[i]=(p-p/i)*1ll*inv[p%i]%p; printf("%lld\n",inv[i]); } return 0; }