把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【学习笔记】$gcd$ 与扩展 $gcd$

前言

模板测试!我!居然!没写出来!这足以证明之前的我学习方法是有多蠢 \(/kk\)

重学重学唉/(ㄒoㄒ)/~~

声明:本博客所有随笔都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

\(\\\)


\(\\\)

\(gcd\)

欧几里得算法也叫辗转相除法,是用来求两个数的最小公倍数的。

主要运用的定理:

\[\gcd(a, b) = \gcd(b, a\%b) \]

特殊的,当 \(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\)

\[ax + by = bx_1 + (a \% b)y_1 \]

\[ax + by = bx_1 + (a - (a / b) * b)y_1 \]

\[ax + by = bx_1 + ay_1 - (a / b) * by_1 \]

\[ax + by = ay_1 + b(x_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 = x_1 + \frac{b}{\gcd(a, b)} * k, y = y_1 - \frac{a}{\gcd(a, b)} * k \]

显然,这样的解是无数的,我们主要来讨论一下正整数解,即 \((x, y)\) 满足 \(x > 0, y > 0\)

\(3rd.\) 关于 \(ax + by = c\) 的正整数解的讨论

再复制一下方程的解的通式

\[x = x_1 + \frac{b}{\gcd(a, b)} * k, y = y_1 - \frac{a}{\gcd(a, b)} * k \]

则可得到两个不等式:

\[\begin{cases}y_1 - \frac{a}{\gcd(a, b)} * k > 0\\x_1 + \frac{b}{\gcd(a, b)} * k > 0\end{cases} \]

解得

\[- \frac{x_1\gcd(a, b)}{b} < k < \frac{y_1\gcd(a, b)}{a} \]

要求 \(k\) 为整数,即

\[\lceil- \frac{x_1\gcd(a, b)}{b}\rceil < k < \lfloor \frac{y_1\gcd(a, b)}{a}\rfloor \]

即可求出 \(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;
}

完结撒花✿✿ヽ(°▽°)ノ✿

posted @ 2020-05-10 22:32  Bn_ff  阅读(183)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end