zoukankan      html  css  js  c++  java
  • [数论]拓展欧几里得算法

    欧几里得算法(辗转相除法)

    用来求解最大公约数

    1 int gcd(int a,int b){
    2      return b ? gcd(b,a%b) : a;
    3 }

    在 #include<algorithm> 中也可以直接调用 __gcd(a,b)


    拓展欧几里得算法

    求解不定方程:

    引理:存在 x , y 使得 ax+by=gcd(a,b)

    设a,b,c为任意整数,若方程ax+by=c的一组解是(x0,y0),则它的任意整数解都可以写成(x0+k*b/gcd(a,b),y0-k*a/gcd(a,b)),k取任意整数

     1 typedef long long ll;
     2 
     3 ll exgcd(ll a,ll b)
     4 {
     5     if(b){
     6         ll r=exgcd(b,a%b);
     7         ll k=x;
     8         x=y;
     9         y=k-a/b*y;
    10         return r;
    11     }
    12     else{
    13         x=1,y=0;
    14         return a;
    15     }
    16 }

    解线性同余方程

    关于 x 的模方程 ax%b=c 的解,方程转换为 ax+by=c 其中 y 一般为非正整数,则问题变为用 exgcd 解不定方程


    计算乘法逆元 

    若 a*x≡1(mod b) ,且 a与 b互质,那么我们就能定义: x为 a的逆元

    这就是利用拓欧求解线性同余方程 a*x≡c(mod b) 的 c=1 的情况。我们就可以转化为解 a*x + b*y = 1这个方程

     1 typedef long long ll;
     2 
     3 void exgcd(ll a,ll b)//扩展欧几里得算法求乘法逆元,x为a的逆元
     4 {
     5     if(!b){
     6         x=1,y=0;
     7         return;
     8     }
     9     exgcd(b,a%b);
    10     ll k;
    11     k=x;
    12     x=y;
    13     y=k-(a/b)*y;
    14 }

    一些例题

    zoj 3609

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 ll gcd;
     5 
     6 ll exgcd(ll a,ll b,ll &x,ll &y)
     7 {
     8     if(b==0){
     9         x=1,y=0;
    10         return a;
    11     }
    12     ll ans=exgcd(b,a%b,x,y);
    13     ll temp=x;
    14     x=y;
    15     y=temp-a/b*y;
    16     return ans;
    17 }
    18  
    19 ll cal(ll a,ll b,ll c)
    20 {
    21     ll x,y;
    22     gcd=exgcd(a,b,x,y);
    23     ll ans=(x%b+b)%b;
    24     if(!ans)
    25         ans=b;
    26     return ans;
    27 }
    28  
    29 int main()
    30 {
    31     ll a,b,t;
    32     cin>>t;
    33     while(t--){
    34         cin>>a>>b;
    35         ll ans=cal(a,b,1);
    36         if(gcd!=1)
    37             cout<<"Not Exist
    ";
    38         else 
    39             cout<<ans<<endl;
    40     }
    41     return 0;
    42 }

    zoj 3593

     1 /*根据公式ax+by=c;当x,y同号时等于max(x,y)
     2 当a,b异号时等于(abs(x)+abs(y))
     3 因为a,b大于0,所以不管x,y同号还是异号都是当x,y,最接近时,答案最小
     4 写出通式 x=x1+b/(gcd)*k, y=y1-a/gcd*k
     5 假设x,y相等, k=(y-x)/(a+b),因为x,y不一定相等,所以枚举k附近的点*/
     6 #include<bits/stdc++.h>
     7 using namespace std;
     8 typedef long long ll;
     9 ll x,y;
    10 
    11 ll exgcd(int a,int b){
    12     if(b){
    13         ll n=exgcd(b,a%b);
    14         ll xt=x;
    15         x=y,y=xt-a/b*y;
    16         return n;
    17     }
    18     x=1,y=0;
    19     return a;
    20 }
    21 
    22 int main(){
    23     ll n,m,t,a,b,c;
    24     cin>>t;
    25     while(t--){
    26         cin>>n>>m>>a>>b;
    27         c=n-m;
    28         ll ans=exgcd(a,b);
    29         if(c%ans!=0){
    30             cout<<-1<<endl;
    31             continue;
    32         }
    33         c/=ans,a/=ans,b/=ans;
    34         x*=c,y*=c;
    35         ll k=(y-x)/(a+b);
    36         ans=10000000000ll;
    37         for(ll i=k-1;i<k+2;i++){
    38             ll xx;
    39             ll x0=x+b*i,y0=y-a*i;
    40             if(x0<=0&&y0>=0||y0<=0&&x0>=0)
    41                 xx=abs(x0)+abs(y0);
    42             else
    43                 xx=max(abs(x0),abs(y0));
    44             if(xx<ans)
    45                 ans=xx;
    46         }
    47         cout<<ans<<endl;
    48     }
    49     return 0;
    50 }

    poj 1061

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 typedef long long ll;
     5 ll x,y;
     6 
     7 ll exgcd(ll a,ll b)
     8 {
     9     if(b){
    10         ll r=exgcd(b,a%b);
    11         ll k=x;
    12         x=y;
    13         y=k-a/b*y;
    14         return r;
    15     }
    16     else{
    17         x=1,y=0;
    18         return a;
    19     }
    20 }
    21 
    22 int main()
    23 {
    24     ll p,q,m,n,l;
    25     scanf("%lld%lld%lld%lld%lld",&p,&q,&m,&n,&l);
    26         ll a=n-m,b=l,c=p-q;
    27         ll ans=exgcd(a,b);
    28         if(c%ans)
    29             printf("Impossible
    ");
    30         else{
    31             ll k=c/ans,t=b/ans;
    32             x*=k;
    33             if(t<0)
    34                 t=-t;
    35             x=(x%t+t)%t;
    36             cout<<x<<endl;
    37         }
    38     return 0;
    39 }

    hdu 1576

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll x,y;
    
    ll exgcd(int a,int b){
        if(b){
            ll n=exgcd(b,a%b);
            ll xt=x;
            x=y,y=xt-a/b*y;
            return n;
        }
        x=1,y=0;
        return a;
    }
    
    int main(){
        ll n,m,t,a,b=9973,c;
        cin>>t;
        while(t--){
            cin>>n>>a;
            ll ans=exgcd(a,b);
            x=(x%b+b)%b;
            ans=n*(x%b)%b;
            cout<<ans<<endl;
        }
        return 0;
    }
    正因为是最弱,所以才理解智慧之强
  • 相关阅读:
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 133 克隆图
  • 原文地址:https://www.cnblogs.com/Yanick/p/11412108.html
Copyright © 2011-2022 走看看