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\) 就是其中的一组解(用了指针)。
例题:
这道题算是模板题,但是还是要处理一些特殊情况。
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;
}

浙公网安备 33010602011771号