zoukankan      html  css  js  c++  java
  • 【模板】逆元求法总结

    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
    ");
    10     for(int i=2;i<=n;i++){
    11         inv[i]=(long long)(p-p/i)*inv[p%i]%p;
    12         printf("%lld
    ",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 }
    线性求阶乘逆元
  • 相关阅读:
    java Exception in thread "main" java.lang.NoClassDefFoundError: main (wrong name: pm/main)
    javac 编译java文件提示: 程序包com.mysql.jdbc不存在
    Java开发笔记(二十四)方法的组成形式
    Java开发笔记(二十三)数组工具Arrays
    Java开发笔记(二十二)神奇的冒号
    Java开发笔记(二十一)二维数组的扩展
    Java开发笔记(二十)一维数组的用法
    Java开发笔记(十九)规律变化的for循环
    Java开发笔记(十八)上下求索的while循环
    Java开发笔记(十七)各得其所的多路分支
  • 原文地址:https://www.cnblogs.com/JKAI/p/7806456.html
Copyright © 2011-2022 走看看