0%

二元一次不定方程(exgcd)

前置知识

扩展欧几里得算法(辗转相除法)

a,bN+\forall a,b \in \mathbb{N^+},有

gcd(a,b)=gcd(b,amodb)\gcd(a,b)=\gcd(b, a\bmod b)

裴蜀定理

a,bN+\forall a,b\in \mathbb{N^+}x,yZ\exists x,y\in \mathbb{Z},使得

ax+by=gcd(a,b)ax+by=\gcd(a,b)

问题引入

给定不定方程

ax+by=gcd(a,b)ax+by=\gcd(a,b)

求出 x,yx,y 的一组解。

解法:我们令

by+(amodb)x=gcd(b,amodb)by'+(a\bmod b)x'=\gcd(b, a\bmod b)

又因为

amodb=aabba\bmod b=a-\lfloor \dfrac{a}{b} \rfloor b

代入得

ax+b(yabx)=gcd(b,amodb)ax'+b(y'-\lfloor \dfrac{a}{b} \rfloor x')=\gcd(b,a\bmod b)

gcd(a,b)=gcd(b,amodb)\gcd(a,b)=\gcd(b, a\bmod b),得

{x=xy=yabx \left\{ \begin{aligned} x&=x'\\ y&=y'-\lfloor \dfrac{a}{b} \rfloor x' \end{aligned} \right.

由于在辗转相除法的最后,必定是 a=gcd(a,b),b=0a'=\gcd(a,b),b'=0,则此时 x=1,y=0x'=1,y'=0。那么我们可以在递归回溯的时候逐层求出所求 x,yx,y,得到如下代码:

1
2
3
4
5
6
7
8
9
void exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0) {
x = 1; y = 0;
return;
}
exgcd(b, a % b, y, x);
y = y - a / b * x;
}

注意 x,yx,y 为传址调用。

现在考虑新的问题:等式右边 gcd(a,b)\gcd(a,b) 被替换为正整数 cc,求解的数量、x,yx,y 的最大最小值。

把上文代码求得的 x,yx,y 分别乘上 cgcd(a,b)\dfrac{c}{\gcd(a,b)} 即为方程的一组解。

引理 1:gcd(a,b)c\gcd(a,b) | cax+by=cax+by=c 有整数解的充分必要条件。

证明:设 d=gcd(a,b)d=\gcd(a,b)a=k1da=k_1db=k2db=k_2d,则 k1x+k2y=cdk_1x+k_2y=\dfrac{c}{d}。左边是整数,因此 cdZ\dfrac{c}{d}\in \mathbb{Z},即 dcd | c,充分性得证。由 k1,k2k_1,k_2 互质,所以 k1x+k2y=1k_1x'+k_2y'=1 一定有整数解。由 cdZ\dfrac{c}{d}\in \mathbb{Z},得原方程的一组解为

{x=xcdy=ycd \left\{ \begin{aligned} x&=x'\dfrac{c}{d}\\ y&=y'\dfrac{c}{d} \end{aligned} \right.

必要性得证。

由此我们得到了判断该方程是否有解的方法。

引理 2:设 x0,y0x_0,y_0ax+by=cax+by=c 的解,那么所有的解都可表示为

{x=x0+k2ty=y0k1t(tZ) \left\{ \begin{aligned} x&=x_0+k_2t\\ y&=y_0-k_1t (t\in \mathbb{Z}) \end{aligned} \right.

x,yx,y 代入方程,容易验证。

上式变换后,可知当

x0+1k2ty01k1\lceil \dfrac{-x_0+1}{k_2} \rceil \le t \le \lfloor \dfrac{y_0-1}{k_1} \rfloor

时,x,yx,y 均为正整数。

x0+1k2>y01k1\lceil \dfrac{-x_0+1}{k_2} \rceil > \lfloor \dfrac{y_0-1}{k_1} \rfloor,则原不定方程无正整数解。

显然 xx 越大,yy 越小,反之亦然。且正整数解的个数为 max{0,y01k1x0+1k2+1}\max\{0,\lfloor \dfrac{y_0-1}{k_1} \rfloor-\lceil \dfrac{-x_0+1}{k_2} \rceil+1\}

题目应用

到这里我们就可以解决下面这些题了。

【模板】二元一次不定方程 (exgcd)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll T, a, b, c, d, t, cnt;

void exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0) {
x = 1; y = 0;
return;
}
exgcd(b, a % b, y, x);
y = y - a / b * x;
}

void solve()
{
ll x, y, l, r;
scanf("%lld%lld%lld", &a, &b, &c);
d = __gcd(a, b);
if (c % d) {
printf("-1\n");
return;
}
exgcd(a, b, x, y);
x *= c / d; y *= c / d;
a /= d; b /= d;
l = ceil((1.0 - double(x)) / (double)b);
r = floor(((double)y - 1.0) / (double)a);
if (l <= r)
printf("%lld %lld %lld %lld %lld\n", r - l + 1, x + b * l, y - a * r, (c - b * d * (y - a * r)) / (a * d), (c - a * d * (x + b * l)) / (b * d));
else
printf("%lld %lld\n", x + b * l, y - a * r);
}

int main()
{
scanf("%lld", &T);
while (T--) solve();
return 0;
}

[NOIP2012 提高组] 同余方程

同余方程 axc(modb)ax\equiv c \pmod b 其实就是 ax+by=cax+by=c。答案为 xx 的最小正整数解。