前言
模板测试!我!居然!没写出来!这足以证明之前的我学习方法是有多蠢 (/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_1gcd(a, b)}{b} ceil)
(y) 的最小正整数取值的 (k) 就为 (lfloorfrac{y_1gcd(a, b)}{a} floor)
(\)
(\)
模板题
丑陋的代码:
#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
", k2 - k1 + 1, minx, miny, maxx, maxy);
else printf("%lld %lld
", 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;
}
完结撒花✿✿ヽ(°▽°)ノ✿