扩展欧几里得
扩展欧几里得(exgcd)是普通欧几里得的升级版,它不仅能有来求gcd(a,b) = d,还可以用来求ax + by = c的通解
它的原理是先求出ax + by = d的一组解,再通过这组解求出通解
流程是这样的:
设a = q * b,r = a % b = a - q * b
我们先求出最下面一层的解
即求出b * x + r * y = d的解
再将r = a - q * b代入后可以得到a * lasty + b * (lastx - q * lasty) = d
所以我们可以得到x = lasty,y = lastx - q * lasty这个递推式
那么边界是什么呢?
由普通欧几里得的流程可以知道,当b | a时,下一层a = b = d,b = 0,r = 0,所以此时b * x + r * y的解是x = 1,y = 0
然后我们就可以把x = 1,y = 0愉快地带回去了
最后我们得到一个解a * x0 + b * y0 = d
如何得到通解呢?
设x1,y1使a * x1 + b * y1 = d吧
两个式子相减,可得a * (x0 - x1) = -b * (y0 - y1)
再设a' = a / d,b' = b / d
将上面那个式子/d得到 a' * (x0 - x1) = -b * (y0 - y1)
可得a' | b * (y0 - y1),gcd(a',b') = 1
最后得到a' | (y0 - y1)
设a' * t = y0 - y1
带回原式得x0 - x1 = -b * t
得到x1 = x0 + b * t,y1 = y0 - a * t
通解就长这样了
例如这道例题
题目让我们求a * x mod b = 1中的正整数x的最小值
这个题目其实可以转化成a * x + b * y = 1,y是负数
设gcd(a,b) = d,a = d * k1,b = d * k2
上面那个式子就能转化成为d * (k1 * x + k2 * y) = 1
所以d = 1
所以a' = a,b' = b
那么这个问题就好办多了
先按照上面的步骤,得到一组解x0,y0
所以x1 = x0 + b * t
因为x1 > 0,所以x0 + b * t > 0
得到t > x0 / b
因此t = x0 / b + 1
但是因为有时x0 / b是负数,会自动向上取整,导致t比合法的大
所以还要对x1取模
记得开long long
代码如下
#include <bits/stdc++.h>
#define int long long
using namespace std;
int x,y,q,t,a,b;
void exgcd(int a,int b){
if (b == 0){
x = 1,y = 0;
return ;
}
exgcd(b,a % b);
int q = a / b;
int x1 = y,y1 = x - q * y;
x = x1,y = y1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> a >> b;
exgcd(a,b);
t = x / b + 1;
cout << (x + b * t) % b;
return 0;
}
浙公网安备 33010602011771号