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 }
  • 相关阅读:
    vue 传参动态
    a href="tel" 拨打电话
    vue中rem的转换
    请求接口的封装
    http request 请求拦截器,有token值则配置上token值
    node溢出
    vue菜单切换
    vue的table切换
    vue页面初始化
    [论文笔记] Legacy Application Migration to the Cloud: Practicability and Methodology (SERVICES, 2012)
  • 原文地址:https://www.cnblogs.com/robertspot/p/12387235.html
Copyright © 2011-2022 走看看