Exgcd的笔记

EXGCD 拓展欧几里得算法

明明算是个基础知识为啥会没学过

我们知道gcd求的是最小公倍数,可以运用裴蜀定理求一个二元一次方程是否有解。

那么现在给定一个形如 \(ax+by=c\) 的方程,让你求 \(x和y\)的值,该怎么求呢?

这就引入了 \(exgcd\) 这个算法。

exgcd通常用来求形如\(ax+by=gcd(a,b)\)的方程

过程:

\(ax_1+by_1=gcd(a,b)\)

\(bx_2+(a\mod b)y_2=gcd(b,a\mod b)\)

通过欧几里得算法可以转化为:

\(gcd(b,a\mod b)=gcd(a,b)\)

因此 \(ax_1+by_1=bx_2+(a\mod b)y_2\)

又因为 \(a\mod b=a-(\lfloor \frac{a}{b}\rfloor*b)\)

所以 \(ax_1+by_1=bx_2+(a-(\lfloor\frac{a}{b}\rfloor)*b)y_2\)

因此两边可以转化成:\(ax_1+by_1=ay_2+b(x_2-\lfloor\frac{a}{b}\rfloor y_2)\)

因为 \(a=a,b=b\) 所以\(x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2\)

然后就是代码:

ll exgcd(ll a,ll b,ll &x,ll &y) {
  if (!b) {
    x = 1;
    y = 0;
    return a;
  }
  ll d = exgcd(b, a % b, x, y);
  ll t = x;
  x = y;
  y = t - (a / b) * y;
  return d;
}

返回的 \(d\)\(gcd(a,b)\) 的值,\(x,y\) 就是其中的一组解(用了指针)。

例题:

luogu P5656

这道题算是模板题,但是还是要处理一些特殊情况。

1.如果 \(ax+by=c\)\(c\) 不是 \(gcd(a,b)\) 的倍数,那么直接不成立,输出 \(-1\)

2.如果有解的话,我们可以先找出来一组解,然后对其进行判断:

如果 \(x<0\) 那么我们就要把 \(x\) 上调到最大,此时 \(y\) 取到最小。

如果 \(x>=0\) 那么我们就要把 \(y\) 上调到最大,此时 \(x\) 取到最小。

更详细的可以看离散小波变换 的题解QAQ,我基本上是按着他的思路做的。

接下来是代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
    ll res=0,op=1;char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') op=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0'){
        res=(res<<3)+(res<<1)+ch-'0';
        ch=getchar();
    }
    return res*op;
}
ll exgcd(ll a,ll b,ll &x,ll &y) {
  if (!b) {
    x = 1;
    y = 0;
    return a;
  }
  ll d = exgcd(b, a % b, x, y);
  ll t = x;
  x = y;
  y = t - (a / b) * y;
  return d;
}
int T;
int main()
{
    T=read();
    while(T--){
        ll a=read(),b=read(),c=read(),x,y;
        ll d=exgcd(a,b,x,y);
        if(c%d) puts("-1");
        else{
            x*=c/d,y*=c/d;
            ll p=b/d,q=a/d,k;
            if(x<0)         k=ceil((1.0-x)/p),x+=p*k,y-=q*k;//k为需要上调几组解使得x最大,y最小
            else if(x>=0)   k=(x-1)/p,x-=p*k,y+=q*k;//将x调到最小,此时y最大
            if(y>0){
                printf("%lld %lld %lld %lld %lld\n",(y-1)/q+1,x,(y-1)%q+1,x+(y-1)/q*p,y);
            }
            else{
                printf("%lld %lld\n",x,y+q*(ll)ceil((1.0-y)/q));
            }
        }
    }
    system("pause");
    return 0;
}
posted @ 2021-03-16 18:22  Evitagen  阅读(59)  评论(0)    收藏  举报