扩展欧几里得算法
AcWing 877. 扩展欧几里得算法
给定 \(n\) 对正整数 \(a_i, b_i\),对于每对数,求出一组 \(x_i, y_i\),使其满足 \(a_i \times x_i + b_i \times y_i = gcd(a_i, b_i)\)。
输入格式
第一行包含整数 \(n\)。
接下来 \(n\) 行,每行包含两个整数 \(a_i, b_i\)。
输出格式
输出共 \(n\) 行,对于每组 \(a_i, b_i\),求出一组满足条件的 \(x_i, y_i\),每组结果占一行。
本题答案不唯一,输出任意满足条件的 \(x_i, y_i\) 均可。
数据范围
\(1 \le n \le 10^5\),
\(1 \le a_i,b_i \le 2 \times 10^9\)
输入样例:
2
4 6
8 18
输出样例:
-1 1
-2 1
题解
思路
前导题
- 
利用辗转相除法求最大公约数,也就是欧几里得算法(gcd算法)
 - 
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } - 
辗转相除法里面有一个注意点
gcd(a, b)的下一层一定是gcd(b, a % b),要进行翻转,因为我们实际上是将a%b,b不变,a%b的结果一定是小于b的,如果还写成gcd(a % b, b),那么下一层函数会执行(a % b) % b结果永远等于a % b,就会导致无限递归,所以要进行翻转,将更小的a % b放在后面。 - 
- 
举个例子
 - 
- \((78,14)=(14×5+8, 14)=(8, 14)\),在进入下一层函数时,就应写成\(gcd(14, 8)\),这样才能正确执行 \(14 mod 8\) 的操作
 
 
 - 
 
本题题意
求出一组 \(x_i, y_i\),使其满足 \(a_i \times x_i + b_i \times y_i = gcd(a_i, b_i)\)。
前置知识
推导
- 
我们在给
gcd()传入参数时,将我们要求的\(x, y\)也传入,则第一层gcd()就是gcd(a, b, x, y) - 
\(a_i \times x_i + b_i \times y_i = gcd(a_i, b_i)\) 进入下一层
gcd()时,变成了gcd(b, a % b),那么本题对应的系数\(x, y\)也要翻转,就变为gcd(b, a % b, y, x) - 
把
gcd(b, a % b, y, x)的表达式根据 \(a_i \times x_i + b_i \times y_i = gcd(a_i, b_i)\)剖析开也就是 \(b \times y + (a \mod b) \times x\) - 
但我们标准的\(x, y\)系数应该是对应的
a和b, \(a_i \times x_i + b_i \times y_i = gcd(a_i, b_i)\)也就是说这里的y和x相比于上一层gcd(a, b, x, y)改变了,我们将其拆开重新去找对应于a和b的系数x, y拆分 \(b \times y + (a \mod b) \times x = gcd(b_i, a_i \mod b_i)\)
- \(a \mod b = a - \lfloor\frac{a}{b}\rfloor \times b\)
 - 拆开
 - \(b \times y + (a - \lfloor\frac{a}{b}\rfloor \times b) \times x\)
 - \(a \times x + b \times (y - \lfloor\frac{a}{b}\rfloor \times x)\)
 - 新的\(x = x\)
 - 新的\(y = y - \lfloor\frac{a}{b}\rfloor \times x\)
 x没变,只有y变了,只用改变y
 
代码实现
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
int a, b, x, y;
int exgcd(int a, int b, int &x, int &y) //注意这里x和y一定要以引用方式传入,否则不会改变x和y本来的值,传不回solve()
{
    //辗转相除,直到小括号内右边数为0
    if(!b) //当b等于0找到了最大公约数a
    {
        //此时让系数x=1, y=0, 就能满足题目要求a*1 + b*0=gcd(a, b)=a 但往前返回的时候x和y会改变,这里的特判是为了往回递归的时候方便更新y的值
        x = 1, y = 0;
        return a;
    }
    
    //这里初始化d=gcd(b, a % b, y, x)的原因是要让gcd() 上一层递归返回后,都更改一次下面的系数y
    int d = exgcd(b, a % b, y, x);
    
    //重新计算系数y
    y -= (a / b) * x;
    
    return d;
}
void solve()
{
    cin >> a >> b;
    exgcd(a, b, x, y);
    
    cout << x << " " << y << endl;
    
    return;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> n;
    while (n -- )
    {
        solve();
    }
    
    return 0;
}

                
            
        
浙公网安备 33010602011771号