【学习笔记】$gcd$ 与扩展 $gcd$
前言
模板测试!我!居然!没写出来!这足以证明之前的我学习方法是有多蠢 \(/kk\)
重学重学唉/(ㄒoㄒ)/~~
声明:本博客所有随笔都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。
\(\\\)
\(\\\)
\(gcd\)
欧几里得算法也叫辗转相除法,是用来求两个数的最小公倍数的。
主要运用的定理:
特殊的,当 \(b = 0\) 时,\(\gcd(a, b) = a\)
于是可以递归下去直到 \(b = 0\)
时间复杂度是 \(O(\log n)\) 级别的,但具体的要运用的一些奇怪定理,暂时略过了。
\(Code:\)
int gcd(int a, int b){return b == 0 ? a : gcd(b, a % b);}
\(\\\)
\(\\\)
扩展 \(gcd\)
一步一步来推。
\(1st.\) 对于 \(ax + by = \gcd(a, b)\) 这个方程, \(x, y\) 有整数解
根据欧几里得算法可得,\(ax + by = \gcd(a, b) = \gcd(b, a \% b) = bx_1 + (a \% b)y_1\)
即
于是可以得到,\(x = y_1, y = (x_1 - (a / b)y_1)\)
特殊的,当 \(b = 0\) 时,\(x = 1, y = 0\)
于是可以一直递归下去到 \(b = 0\) 时,在返回不断更新解
\(Code:\)
int exgcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
int res = exgcd(b, a % b, x, y), tmp = y;
y = x - (a / b) * y, x = tmp;
return res;
}
\(\\\)
\(2nd.\) 对于一般方程 \(ax + by = c\) 的求解
首先,\(c\) 必须是 \(\gcd(a, b)\) 的倍数,可以理解为,把方程左边提一个因数 \(\gcd(a, b)\) 出来,那么右边也得有这个因数
在第一步时,我们对于特殊方程 \(ax + by = \gcd(a, b)\) 求出了一组唯一解 \(x_0, y_0\)
设 \(c / \gcd(a, b) = k\),那么方程的一组特殊解为 \(x_1 = x_0 * k, y_1 = y_0 * k\)
接下来求一般解,其实没有想象的那么难!(感觉网上的博客讲得玄乎其神的...也许是我太菜了 \(/kk\)
设方程某个解为 \(x = x_1 + k, y = y_1 + k'\),即 \(a(x_1 + k) + b(y_1 + k') = c\)
把式子拆开即可得 \(ax_1 + by_1 + ak + bk' = c\)
发现 \(ak + bk' = 0\) ,把 \(a, b\) 拆开写成 \(\gcd(a, b) * (a'k + b'k') = 0\) ,此时 \(\gcd(a', b') = 1\)
那么仍然要满足上面的式子,就要求 \(k = b', k' = -a'\) ,即 \(k = \frac{b}{\gcd(a, b)}, k' = - \frac{a}{\gcd(a, b)}\)
于是就顺利得到了新的一组解—— \(x = x_1 + \frac{b}{\gcd(a, b)}, y = y_1 - \frac{b}{\gcd(a, b)}\),只要把这组 \(x, y\) 看做 \(x_1, y_1\) ,按照上面的步骤继续做就可以得到其他解了~
然后可以得到解的通式
显然,这样的解是无数的,我们主要来讨论一下正整数解,即 \((x, y)\) 满足 \(x > 0, y > 0\)
\(3rd.\) 关于 \(ax + by = c\) 的正整数解的讨论
再复制一下方程的解的通式
则可得到两个不等式:
解得
要求 \(k\) 为整数,即
即可求出 \(k_{max}, k_{min}\),则方程正整数解的数量就是 \(k_{max} - k_{min} + 1\)
将 \(k_{max}, k_{min}\) 带入解的通式,则可得到 \(x\) 和 \(y\) 的最大最小正整数解
如果不等式无解,则方程无正整数解
那么 \(x\) 的最小正整数取值的 \(k\) 就为 \(\lceil- \frac{x_1\gcd(a, b)}{b}\rceil\)
\(y\) 的最小正整数取值的 \(k\) 就为 \(\lfloor\frac{y_1\gcd(a, b)}{a}\rfloor\)
\(\\\)
\(\\\)
模板题
丑陋的代码:
#include<bits/stdc++.h>
#define ll long long
#define F(i, x, y) for(register int i = x; i <= y; ++ i)
using namespace std;
inline ll read();
ll t, a, b, c, x, y, res;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(b == 0)
{
x = 1, y = 0;
return a;
}
ll res = exgcd(b, a % b, x, y), tmp = y;
y = x - (a / b) * y, x = tmp;
return res;
}
int main()
{
t = read();
while(t --)
{
a = read(), b = read(), c = read();
res = exgcd(a, b, x, y);
if(c % res != 0) {puts("-1"); continue;}
x = x * (c / res);
y = y * (c / res);
ll k1 = ceil(-x * 1LL * res / b);
while(x + b / res * k1 <= 0) ++ k1;
ll k2 = floor(y * 1LL * res / a);
while(y - a / res * k2 <= 0) -- k2;
ll minx = x + b / res * k1;
ll maxx = x + b / res * k2;
ll miny = y - a / res * k2;
ll maxy = y - a / res * k1;
if(k2 >= k1)
printf("%lld %lld %lld %lld %lld\n", k2 - k1 + 1, minx, miny, maxx, maxy);
else printf("%lld %lld\n", minx, miny);
}
return 0;
}
inline ll read()
{
ll x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
完结撒花✿✿ヽ(°▽°)ノ✿