【数论基础】扩欧的应用及乘法逆元

老师讲了3个应用

1.求解不定方程ax+by=c

方程ax+by=c并不总是有解,当且仅当gcd(a,b) | c有解。
由裴蜀定理知一定存在x0,y0使得a*x0+b*y0=gcd(a,b)。则可由exgcd求出x0,y0,
则x1=c/d*x0,y1=c/d*y0为方程ax+by=c的一组解,(d=gcd(a,b))
则方程ax+by=c的通解为
x=x1+b/d*k,y=y1-a/d*k
证明如下:
a*x1+b*y1=a*x+b*y=c
a(x1-x)=b(y-y1)
a/d(x1-x)=b/d(y-y1),此时a/d和b/d一定互素,因此x1-x一定是b/d的整数倍,
设为k*(b/d),同理y-y1一定是a/d的整数倍,设为k*(a/d),因此任意解为(x1+b/d*k,y1-a/d*k),k取任意整数。
 

2.求解模线性方程

对于线性同余方程:ax≡m(mod b) 转化为ax+by=m则可直接求解。
看两个例题
简单讲一下推导过程:
这题要解这个方程         
方程等价于            
再变一下            
令S=x-y,W=n-m(注意这个地方的变号)
得到一个不定方程         
这个题就是求出k的最小值,那么先求出一组特解,再推导出最小解。
由于exgcd的使用时,方程变成了  
该方程的所有解就表示为     
 
由于                                                
则                                                   
而扩欧的解是建立在求gcd(W,l)上的,所以还要乘上一个(S/gcd(W,l))。
代码如下:
#include<cstdio>
#include<iostream>
#include<cmath>
#define int long long
using namespace std;

int xx,yy,x,y,m,n,l;

inline int ex_gcd(int a,int b){//扩展欧几里得 
    if(b==0){xx=1;yy=0;return a;}
    int ans=ex_gcd(b,a%b);
    int t=xx;xx=yy;yy=t-a/b*yy;
    return ans;
}

signed main(){
    cin>>x>>y>>m>>n>>l;
    int S=x-y,W=n-m;
    if(W<0){W=-W,S=-S;}
    int gcd=ex_gcd(W,l);
    int mod=l/gcd;
    if(S%gcd!=0)    cout<<"Impossible"<<endl;
    else cout<<(xx*(S/gcd)%mod+mod)%mod<<endl;
    return 0;
}

例二

详细题解看这里

这个题的范围很小,可以直接从小到大枚举答案。

枚举两个野人,求出他们在哪一年碰面,得到线性方程如下:

        

转换一下    

于是这里就跟上面那个题一样了,只是需要判断次。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cctype>
#define Max(a,b) a=max(a,b)
using namespace std;

inline int read(){
    int s=0;bool flag=true;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')flag=false;ch=getchar();}
    while(isdigit(ch)){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return flag?s:-s;
}

const int MAXl=1e6;
const int N=20;
int n,maxl,x,y;
int c[N],p[N],l[N];

inline int ex_gcd(int a,int b){
    if(!b){x=1;y=0;return a;}
    int ans=ex_gcd(b,a%b);
    int t=x;x=y;y=t-a/b*y;
    return ans;
}

inline bool check(int i,int j,int len){
    int A=p[j]-p[i],B=c[i]-c[j];
    if(A<0){A=-A,B=-B;}
    int gcd=ex_gcd(A,len);
    int mod=len/gcd;
    if(B%gcd!=0)    return false;
    if((x*(B/gcd)%mod+mod)%mod <= min(l[i],l[j]))    return true;
    return false;
}

signed main(void){
    n=read();
    for(int i=1;i<=n;i++)
        c[i]=read(),p[i]=read(),l[i]=read(),Max(maxl,c[i]);
    for(int i=maxl;i<=MAXl;i++){
        bool flag=false;
        for(int j=1;j<n;j++){
            for(int k=j+1;k<=n;k++)
                if(flag=check(j,k,i))    break;
            if (flag)    break;
        }
        if (!flag){cout<<i<<endl;return 0;}
    }
}

3.求解逆元

存在x使得ax≡1(mod p),则称x是a关于p的乘法逆元
a关于p的乘法逆元存在的充要条件是gcd(a,p)=1,即a,p互质
如果逆元存在,则在 0 到 p − 1 范围内有且仅有一个逆元。
当要求(a/b)%p时,且a很大,我们就求b关于p的乘法逆元x,则有(a/b)%p = (a*x)%p
 
求逆元就是用扩欧的,下面是纯板子:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cctype>
using namespace std;

inline int read(){
    int s=0;bool flag=true;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')flag=false;ch=getchar();}
    while(isdigit(ch)){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return flag?s:-s;
}

inline void out_put(int x){
    if(x<0)    x=-x,putchar('-');
    if(x>9)    out_put(x/10);
    putchar(x%10+'0');
}

inline void print(int x){out_put(x),puts("");}

int a,b,x,y;

inline void ex_gcd(int a,int b){//扩展欧几里得 
    if(b==0){x=1;y=0;return ;}
    ex_gcd(b,a%b);
    int t=x;x=y;y=t-a/b*y;
}

signed main(){
    a=read(),b=read();
    ex_gcd(a,b);
    print((x+b)%b);//保证实际意义,可能为负数 
    return 0;
}

线性推逆元

这种方法只能在p为质数时才能用

转载该博客

我这里解释一下:

已知         

设        

可得        

两边同时乘得:

  

就会得到如下的线性公式:

inv[1]=1;
for(int i=2;i<=n;++i)
    inv[i]=(p-p/i)*inv[p%i]%p;

代码就很简单了:

#include<cstdio>
#include<iostream>
using namespace std;
inline void out_put(int x){
    if(x<0)    x=-x,putchar('-');
    if(x>9)    out_put(x/10);
    putchar(x%10+'0');
}
inline void print(int x){out_put(x),puts("");}
int main(){
    int n,p;cin>>n>>p;
    long long inv[n+5];
    inv[1]=1;cout<<1<<endl;
    for(int i=2;i<=n;++i){
        inv[i]=(p-p/i)*inv[p%i]%p;
        print(inv[i]);
    }
    return 0;
}

 

p=iq+r(0<r<p)

posted @ 2020-04-30 15:06  初学者Ming  阅读(338)  评论(0编辑  收藏  举报