求逆元模板

方法一——费马小定理求逆元:

(前提是p为质数,否则不能用)根据费马小定理,当gcd(a,p)=1时,有a^(p-1)=1 (mod p),即a*a^(p-2)=1 (mod p),即a关于p的逆元为a^(p-2),用快速幂即可求。

 1 LL pow_mod(LL a, LL b, LL p){//a的b次方求余p 
 2     LL ret = 1;
 3     while(b){
 4         if(b & 1) ret = (ret * a) % p;
 5         a = (a * a) % p;
 6         b >>= 1;
 7     }
 8     return ret;
 9 }
10 LL Fermat(LL a, LL p){//费马求a关于b的逆元 
11         return pow_mod(a, p-2, p);
12 }

方法二——扩展欧基里德求逆元:

若gcd(a,b)=1,则a*x+b*y=1有解,其解的x即为a关于b的逆元,y为b关于a的逆元。

  证明:a*x+b*y=1  ->   a*x%b+b*y%b=1%b  ->  a*x%b=1%b  ->  a*x=1 (mod b),即x为a关于b的逆元,同理可证y。

 1 #include<cstdio>
 2 typedef long long LL;
 3 void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){
 4     if (!b) {d = a, x = 1, y = 0;}
 5     else{
 6         ex_gcd(b, a % b, y, x, d);
 7         y -= x * (a / b);
 8     }
 9 }
10 LL inv(LL t, LL p){//如果不存在,返回-1 
11     LL d, x, y;
12     ex_gcd(t, p, x, y, d);
13     return d == 1 ? (x % p + p) % p : -1;
14 }
15 int main(){
16     LL a, p;
17     while(~scanf("%lld%lld", &a, &p)){
18         printf("%lld\n", inv(a, p));
19     }
20 }

方法三:

(前提为p是质数,否则不能用)inv(a) = (p - p / a) * inv(p % a) % p,然后一直递归到1即可,因为1的逆元是1。

  证明:
    设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

这个方法不限于求单个逆元,比前两个好,它可以在O(n)的复杂度内算出n个数的逆元。

 1 #include<cstdio>
 2 typedef long long LL;
 3 LL inv(LL t, LL p) {//求t关于p的逆元,注意:t要小于p,最好传参前先把t%p一下 
 4     return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p;
 5 }
 6 int main(){
 7     LL a, p;
 8     while(~scanf("%lld%lld", &a, &p)){
 9         printf("%lld\n", inv(a%p, p));
10     }
11 }
1 const int M=10007;
2 const int N=301; 
3 int inv[M],mat[N][N];
4 void init(){//求逆元
5     inv[1]=1;
6     for(int i=2;i<M;i++)
7         inv[i]=(M-M/i)*inv[M%i]%M;
8 }
posted @ 2019-10-16 16:19  古比  阅读(178)  评论(0编辑  收藏  举报