求解逆元
蒙哥马利快速模:
这个算法的局限性很大,只有在模数p 是质数的情况下才可以使用。
首先我们设inv(a)是a的逆元那么根据定义, inv(a)∗a≡1(mod p)再根据费马小定理 a^(p−1)≡1%p , 易得 inv(a)∗a≡a^(p−1)(mod p)
移项,得: inv(a)≡a^(p−2)
于是我们得到了快速幂模算法的一个前提条件: inv(a)≡a^(p−2)(mod p)
代码:
1 #include <bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 const int mod=1e9+7;//质数
5 ll quick_pow(ll x,int p){
6 ll res=1;
7 while(p){
8 if(p&1) res=(res*x)%mod;
9 x=(x*x)%mod;
10 p>>=1;
11 }
12 return res;
13 }
14 ll inv(ll a){
15 ll inv_a=quick_pow(a,mod-2);
16 return inv_a;
17 }
18 int main(){
19 ll a;
20 cin>>a;
21 cout<<inv(a)<<endl;
22 return 0;
23 }
扩展欧几里得求逆元:
证明
考虑两个方程:
a*x1+b*y1=gcd(a,b),b*x2+(a%b)*y2=gcd(b,a%b)
由辗转相除法可得到gcd(a,b)= gcd(b,a%b)
从而得到a*x1+b*y1=b*x2+(a%b)*y2
即a*x1+b*y1=b*x2+(a-(a/b)*b)*y2=a*y2+b*(x2-(a/b)*y2)
所以x1=y2,y1=x2-(a/b)*y2.
当a与b互质时gcd(a,b)=1,即a*x+p*y=1.方程式对m取模,(a*x) mod (p)=1 mod p.
所以a的逆元为(x+p)%p.
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int p=9973; 5 ll extgcd(ll a,ll b,ll &x,ll &y){ 6 if(b==0){ 7 x=1; 8 y=0; 9 return a; 10 } 11 ll gcd=extgcd(b,a%b,x,y); 12 ll t=x; 13 x=y; 14 y=t-(a/b)*y; 15 return gcd; 16 } 17 18 int main(){ 19 ll t,a,b,x,y; 20 cin>>t; 21 while(t--){ 22 cin>>a>>b; 23 extgcd(b,p,x,y); 24 cout<<a*(x%p+p)%p<<endl; 25 } 26 return 0; 27 }
递推求解逆元:
求1 到n逆元表
这个比较适合逆元比较多的时候。比如数据范围在1e5内,随机的1e5内的个数的逆元,所以是所有逆元都得求反而会快点。
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e6; 5 ll inv[N]; 6 void xian_inv(int n,int p){ 7 inv[1]=1; 8 for(int i=2;i<=10000;i++){ 9 inv[i]=(ll)(p-p/i)*inv[p%i]%p; 10 } 11 } 12 int main(){ 13 int a; 14 xian_inv(10000,9973); 15 cin>>a; 16 cout<<inv[a]<<endl; 17 return 0; 18 }