1.问题
求解:ax+by=kg。其中:a>0,b>0,g=gcd(a,b),k为整数
2.特解
原问题 求ax+by=g
求bx+(a%b)y=g。
令bx+(a%b)y=g解为x0,y0。则bx0+(a-a/b*b)y0=g,则x=y0,y=x0-a/b*y0。
可见,如果要求ax+by=g,我们可以持续对a,b进行辗转相除,将被除数换成除数,将除数换成余数,得到新方程的解可以推出旧方程的解。什么时候停下呢?易知当除数为0即b==0,便须停止。然而此时我们发现方程变成了:ax+0y=g,它的特解便唾手可得了:x=1,y=0,理由是:此时方程ax+0y=g,这里的a是上一层方程的b,而在上一层中,上一层的a整除了上一层的b,所以g等于上一层的b,(这里是gcd里的知识,还不了解的可以先去了解一下再来看)也就等于此刻的a,gx+0y=g,当然得出x=1,至于y,随便什么都行,这里取了特解y=0。现在我们得到了最后一层的一个特解,可以通过上面给出的代换式递归得到上一层的x和y,最后递归到第一层,就得到ax+by的解啦,再乘个k倍,进而得到原问题的解了。
3.一点心得
裴蜀定理提到,ax+by=c有整数解的充要条件是c为gcd(a,b)的整数倍,这里不予证明,但是能窥见少许它的正确性:在1.中我们说到辗转相除的最后一层是ax+0y=g,这里的a是等于g的,所以我们说x=1,可是如果等式右边不是g或者g的整数倍呢?我们便不能得到x的整数值,至少就辗转相除这个方法来说,方程无整数解。
4.裸码
void exgcd(ll a,ll b,ll& x,ll& y)
{
if(b==0)
{
x=1;
y=0;
return ;
}
exgcd(b,a%b,x,y);
ll x0=x,y0=y;
x=y0;
y=x0-a/b*y0;
}
int main()
{
ll a,b,g; cin>>a>>b>>g;
ll x,y;
exgcd(a,b,x,y);
cout<<"x="<<x<<",y="<<y;
}
运行结果:
5.通解
现在我们知道了方程ax+by=g的一组特解:x=x0,y=y0。怎么导出它的通解呢?
我们设通解为x,y。不妨令x=x0+p,将其代入ax+by=p可得到:x=x0+p,y=y0-a/b*p。然后我们要求的是整数解,所以要满足两点:1.p为整,2.a/b*p为整。然而我们关注a/b最多约分掉一个g,剩下的a/g与b/g必是互质的,所以这个p必须为k*b/g,其中k为整,否则a/b必产生小数,这样,p本身自然也是整数了,条件达成,故最终通解为:x=x0+k*b/g,y=y0-k*a/g。
6.例题
1.大意
求使满足ax%b=1的最小正整数解,我们转换一下题意:ax=by+1,求使满足的最小正整数x。再转换:ax+by=1,求解最小正整数x。
2.思路
我们先拿到特解,再拿到通解:x=x0+k*b/g,再令x0=k*b/g,反解出k,再向下取整,就能得出x了。
3.完整代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2e9+9;
void exgcd(ll a,ll b,ll& x,ll& y)
{
if(b==0)
{
x=1;
y=0;
return ;
}
exgcd(b,a%b,x,y);
ll x0=x,y0=y;
x=y0; y=x0-a/b*y0;
}
ll gcd(ll a,ll b)
{
while(a%b!=0)
{
ll r=a%b;
a=b;
b=r;
}
return b;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
ll a,b; cin>>a>>b;
ll g=gcd(a,b);
ll x,y;
exgcd(a,b,x,y);
x*=g; y*=g;
ll k=floor(x*1.0/(b/g));
cout<<x-k*b/g<<'\n';
return 0;
}