zoukankan      html  css  js  c++  java
  • 集训笔记——数论:gcd与exgcd

    2020年2月29日更新:

    首先求两数gcd有两种方法:

    1. 辗转相除法(较常用)
    2. 更相减损法(有独特的优势但是一般情况下运算较慢)

    gcd代码:

    1 int gcd(int a,int b){
    2     if(b==0) return a;
    3     return gcd(b,a%b);
    4 }

     

    众所周知:

    lcm(a,b)=a*b/gcd(a,b)

    然后我们来解一个如下的方程:

    a*x+b*y=c  ①

    对x,y求解

    (已知a,b,c,x,y均为整数,a,b,c已知)

    裴蜀定理:

    a、b互质的充要条件为存在x,y∈Z,使得a*x+b*y=1

    由此我们还可得到一个结论:

    如果a*x+b*y=c   (已知a,b,c,x,y属于Z,a,b,c已知),则一定有gcd(a,b) | c  (这里的‘ | ’表示被整除,左面为小数,如2|6,3|12)

    即一定有c为gcd的倍数,才保证此方程有解  ②

    如果要解①的方程,就要用到exgcd了!

    现在一步步分析:

    我们来解①的方程,那么显而易见我们可以有结论②

    所以说如果①方程有解,则c可以写成k*gcd(a,b)的形式

    然后我们把a的位置代入b,b的位置代入a%b,故gcd(a,b)就变成了gcd(b,a%b)

    但由于gcd(b,a%b)=gcd(a,b),所以等式右端恒不变

    所以①方程也就变成了求解如下方程:

    b*x‘+a%b*y’=c  ③

    又因为a%b=a-b*(a/b)

    所以③方程又可以变成:

    b*x‘+(a-b*(a/b))*y’=c  ④

    然后进行转化就有:

    a*y‘+b *(x’-b*(a/b)*y‘)= c  ⑤

    所以说解方程①就是在解方程⑤,如果说我们已经知道了方程⑤中的一组解x'和y‘,那么我们就知道了方程①一定存在的一组解:x=y',y=(x’-b*(a/b)*y‘).

    所以说我们就是在求x'和y'就可以了

    然后我们会发现a和b的不断更新就相当于在求gcd的更新的过程,总会有一刻b=0,所以说当b=0时,有a*x=c=k*gcd(a,b),此时x=c/a,并且建议令y=0(原因在后面会讲),然后回溯

    所以我们回溯到上一步时,有令上一步的x=y,y=(x’-b*(a/b)*y‘)

    重复这一过程,知道回到最初的一步。

    解释一下为什么在b==0的时候,建议将y赋值为0

    由于在回溯的过程中,x会被赋值为y,y会被赋值为(x’-b*(a/b)*y‘),如果在b==0时y被赋值为其他值,虽然最后也会求出一组解,但是y会滚雪球似的增大,所以很有可能超出最大合法数值。

    exgcd代码实现:

     1 void exgcd(int a,int b,int &x,int &y){
     2     if(!b){
     3         x=1;y=0;return ;
     4     }
     5     exgcd(b,a%b,x,y);
     6     int temp;
     7     temp=x;
     8     x=y;
     9     y=temp-(a/b)*y;
    10     return ;
    11 }

    然后再讲一下答案的处理:

    我们有了a*x+b*y=c  ①  时的一组x,y的解

    然后我们对①方程进行相应变式:

    故我们就有了a*x+b*y+k*a*b-k*a*b=c的一组解

    对左侧进行合并:

    有 a*(x+k*b)+b*(y-k*a)=c

    所以我们对我们求出的x可以进行相应的处理,我们可以对x减去或加上无数的b,我们也得到了另一个x的解

    所以当我们需要最小的正整数x解时,我们可得:

    1 x=(x%b+b)%b

    当其他的情况时,可以进行相应的操作

    最后列一下两种exgcd的代码:

    • 第一种(真·直译)
     1 void exgcd(int a,int b,int &x,int &y){
     2     if(!b){
     3         x=1;y=0;return ;
     4     }
     5     exgcd(b,a%b,x,y);
     6     int temp;
     7     temp=x;
     8     x=y;
     9     y=temp-(a/b)*y;
    10     return ;
    11 }
    • 第二种(用来背的代码;紫书上的,更短更好背,也更容易让人迷糊)
    1 void exgcd(int a,int b,int &x,int &y){
    2     if(!b){
    3         x=1;y=0;return ;
    4     }
    5     exgcd(b,a%b,y,x);
    6     y-=(a/b)*x;
    7     return ;
    8 }

    exgcd讲完了,现在开始讲题:

    1.洛谷P1082 同余方程

    原题传送门

    原题:a*x+b*y=1 (显然b为负数)

    因为保证有解,所以一定有a,b互质

    这就是典型的用exgcd的问题

    a,b已知

    那么就是求x的最小正整数解了

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int a,b,x,y;
     4 void exgcd(int a,int b,int &x,int &y){
     5     if(!b){
     6         x=1;y=0;return ;
     7     }
     8     exgcd(b,a%b,x,y);
     9     int temp;
    10     temp=x;
    11     x=y;
    12     y=temp-(a/b)*y;
    13     return ;
    14 }
    15 int main(){
    16     scanf("%d%d",&a,&b);
    17     exgcd(a,b,x,y);
    18     printf("%d",(x%b+b)%b);
    19     return 0;
    20 }

    2.lgP1516 青蛙的约会

    原题传送门

    课件分析:

    • 设x0为最终结果,让青蛙相对运动,可列 出式子: 
    • (m - n)x0 = y - x (mod L) 
    • 显然该式可以转化为: 
    • (m - n)x0 + Ly = y - x 
    • 再变换一下变成: 
    • ax + by = c 
    • 变成了熟悉的exgcd问题,正常求解即可。

    我个人分析也和课件差不多

    代码:(莫名其妙WA掉了#5和#10两个数据)

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 ll x,y,m,n,l;
     5 ll nm,xy;
     6 ll t,tmp;
     7 ll gcd(ll a,ll b){
     8     if(b==0) return a;
     9     return gcd(b,a%b);
    10 }
    11 void exgcd(ll a,ll b,ll &x,ll &y){
    12     if(!b){
    13         x=xy/gcd(n-m,l);y=0;return ;
    14     }
    15     exgcd(b,a%b,x,y);
    16     ll temp;
    17     temp=x;
    18     x=y;
    19     y=temp-(a/b)*y;
    20     return ;
    21 }
    22 int main(){
    23     scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l);
    24     nm=n-m;xy=x-y;
    25     l=-l;
    26     if(xy%gcd(n-m,l)!=0) {
    27         printf("Impossible");
    28         return 0;
    29     }
    30     exgcd(nm,l,t,tmp);
    31     printf("%lld",((t%l)+abs(l))%l);
    32     return 0;
    33 }
  • 相关阅读:
    .NET Interop 工具集
    关于正弦波的算法
    Windows Phone 系列 本地数据存储
    Xaml cannot create an instance of “X”
    Windows Phone 系列 使用 MVVM绑定时无法获取当前值
    Windows Phone 系列 应用程序图标无法显示
    Windows Phone 系列 WPConnect无法上网的问题
    Windows Phone 系列 使用 Windows Phone 保存铃声任务
    WP7.5提交应用
    Windows Phone 系列 动态删除ObservableCollection
  • 原文地址:https://www.cnblogs.com/robertspot/p/12387235.html
Copyright © 2011-2022 走看看