【模板】逆元求法总结

1.求单个数(n)的关于模数(p)的逆元:

(1)扩展欧几里得算法

满足条件:n与p互质

推导过程:(a*x)%p==(a/b)%p

-->x%p==1/b-1/(b*p)*p

-->x*b%p==1-1/p*p

-->x*b+1/p*p==1

由于b与p互质,所以gcd(b,p)==1,因此用扩展欧几里得求出的x即为b关于p的逆元。

ps.这里求出的逆元可能为负因此要转正。

代码:

 1 #include<cstdio>
 2 void ex_gcd(int a,int b,int&x,int&y){
 3     if(b==1){
 4         x=0;y=1;return;
 5     }
 6     ex_gcd(b,a%b,x,y);
 7     int t=x;
 8     x=y;
 9     y=t-a/b*y;
10 }
11 int main(){
12     int a,b,p,x,y;
13     scanf("%d %d %d",&a,&b,&p);
14     ex_gcd(b,p,x,y);
15     x=(x%p+p)%p;
16     printf("%d",x);
17     return 0;
18 }
扩欧求逆元

(2)快速幂求逆元

满足条件:p为质数

推导过程:

由费马小定理:ap-1%p=1

-->a*ap-2%p=1

-->ap-2即为a关于p的逆元

ps.这个速度没扩欧快......

代码:

 1 #include<cstdio>
 2 int ksm(long long a,int b,int p){
 3     int res=1;
 4     while(b){
 5         if(b&1)res=res*a%p;
 6         a=a*a%p;
 7         b>>=1;
 8     }
 9     return res;
10 }
11 int main(){
12     int a,b,p,x;
13     scanf("%d %d %d",&a,&b,&p);
14     x=ksm(b,p-2,p);
15     printf("%d",x);
16     return 0;
17 }
快速幂求逆元

2.预处理1~n关于p的逆元:

(1)单个求-->就是枚举每个数单独求逆元,复杂度O(nlog(n)),这里不再赘述。

(2)递推求逆元:

推导过程:易知1的逆元为1。

令t=p/i,r=p%i;

-->t*i+r≡0(mod)p

-->t*i≡-r(mod)p

-->两边同时乘上i-1*r-1

-->i-1=(-t+p)*r-1%p

-->inv[i]=(p-p/i)*inv[p%i]%p

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 typedef long long LL;
 5 long long inv[3000005];
 6 int main(){
 7     int n,p;
 8     scanf("%d %d",&n,&p);
 9     inv[1]=1;printf("1\n");
10     for(int i=2;i<=n;i++){
11         inv[i]=(long long)(p-p/i)*inv[p%i]%p;
12         printf("%lld\n",inv[i]);
13     }
14     return 0;
15 }
线性求逆元

3.预处理1!~n!关于p的逆元:

(1)递推求阶乘时用扩欧或快速幂求解,复杂度O(nlog(n)),也不重复了。

(2)递推求逆元:

推导过程:设n!的逆元为inv[n]

-->inv[n]=(n!)-1

-->inv[n-1]=(n-1)!-1%p=(n!)-1*i%p

复杂度O(log(n)+n)。

代码:

 1 #include<cstdio>
 2 int inv[100005]; 
 3 int ksm(long long a,int b,int p){
 4     int res=1;
 5     while(b){
 6         if(b&1)res=res*a%p;
 7         a=a*a%p;
 8         b>>=1;
 9     }
10     return res;
11 }
12 int main(){
13     int n,p,x;
14     scanf("%d %d",&n,&p);//求1!~n!关于p的逆元 
15     int kk=1;
16     for(int i=2;i<=n;i++)kk=kk*i%p;
17     inv[n]=ksm(kk,p-2,p);
18     for(int i=n-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%p;
19     return 0;
20 }
线性求阶乘逆元

 

posted @ 2017-11-08 21:56  Child-Single  阅读(479)  评论(0编辑  收藏  举报