前置知识
扩展欧几里得算法(辗转相除法)
对 ∀ a , b ∈ N + \forall a,b \in \mathbb{N^+} ∀ a , b ∈ N + ,有
gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b, a\bmod b)
g cd( a , b ) = g cd( b , a m o d b )
裴蜀定理
对 ∀ a , b ∈ N + \forall a,b\in \mathbb{N^+} ∀ a , b ∈ N + ,∃ x , y ∈ Z \exists x,y\in \mathbb{Z} ∃ x , y ∈ Z ,使得
a x + b y = gcd ( a , b ) ax+by=\gcd(a,b)
a x + b y = g cd( a , b )
问题引入
给定不定方程
a x + b y = gcd ( a , b ) ax+by=\gcd(a,b)
a x + b y = g cd( a , b )
求出 x , y x,y x , y 的一组解。
解法:我们令
b y ′ + ( a m o d b ) x ′ = gcd ( b , a m o d b ) by'+(a\bmod b)x'=\gcd(b, a\bmod b)
b y ′ + ( a m o d b ) x ′ = g cd( b , a m o d b )
又因为
a m o d b = a − ⌊ a b ⌋ b a\bmod b=a-\lfloor \dfrac{a}{b} \rfloor b
a m o d b = a − ⌊ b a ⌋ b
代入得
a x ′ + b ( y ′ − ⌊ a b ⌋ x ′ ) = gcd ( b , a m o d b ) ax'+b(y'-\lfloor \dfrac{a}{b} \rfloor x')=\gcd(b,a\bmod b)
a x ′ + b ( y ′ − ⌊ b a ⌋ x ′ ) = g cd( b , a m o d b )
由 gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b, a\bmod b) g cd( a , b ) = g cd( b , a m o d b ) ,得
{ x = x ′ y = y ′ − ⌊ a b ⌋ x ′ \left\{
\begin{aligned}
x&=x'\\
y&=y'-\lfloor \dfrac{a}{b} \rfloor x'
\end{aligned}
\right. ⎩ ⎪ ⎨ ⎪ ⎧ x y = x ′ = y ′ − ⌊ b a ⌋ x ′
由于在辗转相除法的最后,必定是 a ′ = gcd ( a , b ) , b ′ = 0 a'=\gcd(a,b),b'=0 a ′ = g cd( a , b ) , b ′ = 0 ,则此时 x ′ = 1 , y ′ = 0 x'=1,y'=0 x ′ = 1 , y ′ = 0 。那么我们可以在递归回溯 的时候逐层求出所求 x , y x,y x , 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 , y x,y x , y 为传址调用。
现在考虑新的问题:等式右边 gcd ( a , b ) \gcd(a,b) g cd( a , b ) 被替换为正整数 c c c ,求解的数量、x , y x,y x , y 的最大最小值。
把上文代码求得的 x , y x,y x , y 分别乘上 c gcd ( a , b ) \dfrac{c}{\gcd(a,b)} g cd( a , b ) c 即为方程的一组解。
引理 1:gcd ( a , b ) ∣ c \gcd(a,b) | c g cd( a , b ) ∣ c 是 a x + b y = c ax+by=c a x + b y = c 有整数解的充分必要条件。
证明:设 d = gcd ( a , b ) d=\gcd(a,b) d = g cd( a , b ) ,a = k 1 d a=k_1d a = k 1 d ,b = k 2 d b=k_2d b = k 2 d ,则 k 1 x + k 2 y = c d k_1x+k_2y=\dfrac{c}{d} k 1 x + k 2 y = d c 。左边是整数,因此 c d ∈ Z \dfrac{c}{d}\in \mathbb{Z} d c ∈ Z ,即 d ∣ c d | c d ∣ c ,充分性得证。由 k 1 , k 2 k_1,k_2 k 1 , k 2 互质,所以 k 1 x ′ + k 2 y ′ = 1 k_1x'+k_2y'=1 k 1 x ′ + k 2 y ′ = 1 一定有整数解。由 c d ∈ Z \dfrac{c}{d}\in \mathbb{Z} d c ∈ Z ,得原方程的一组解为
{ x = x ′ c d y = y ′ c d \left\{
\begin{aligned}
x&=x'\dfrac{c}{d}\\
y&=y'\dfrac{c}{d}
\end{aligned}
\right. ⎩ ⎪ ⎪ ⎨ ⎪ ⎪ ⎧ x y = x ′ d c = y ′ d c
必要性得证。
由此我们得到了判断该方程是否有解的方法。
引理 2:设 x 0 , y 0 x_0,y_0 x 0 , y 0 为 a x + b y = c ax+by=c a x + b y = c 的解,那么所有的解都可表示为
{ x = x 0 + k 2 t y = y 0 − k 1 t ( t ∈ Z ) \left\{
\begin{aligned}
x&=x_0+k_2t\\
y&=y_0-k_1t (t\in \mathbb{Z})
\end{aligned}
\right. { x y = x 0 + k 2 t = y 0 − k 1 t ( t ∈ Z )
把 x , y x,y x , y 代入方程,容易验证。
上式变换后,可知当
⌈ − x 0 + 1 k 2 ⌉ ≤ t ≤ ⌊ y 0 − 1 k 1 ⌋ \lceil \dfrac{-x_0+1}{k_2} \rceil \le t \le \lfloor \dfrac{y_0-1}{k_1} \rfloor
⌈ k 2 − x 0 + 1 ⌉ ≤ t ≤ ⌊ k 1 y 0 − 1 ⌋
时,x , y x,y x , y 均为正整数。
若 ⌈ − x 0 + 1 k 2 ⌉ > ⌊ y 0 − 1 k 1 ⌋ \lceil \dfrac{-x_0+1}{k_2} \rceil > \lfloor \dfrac{y_0-1}{k_1} \rfloor ⌈ k 2 − x 0 + 1 ⌉ > ⌊ k 1 y 0 − 1 ⌋ ,则原不定方程无正整数解。
显然 x x x 越大,y y y 越小,反之亦然。且正整数解的个数为 max { 0 , ⌊ y 0 − 1 k 1 ⌋ − ⌈ − x 0 + 1 k 2 ⌉ + 1 } \max\{0,\lfloor \dfrac{y_0-1}{k_1} \rfloor-\lceil \dfrac{-x_0+1}{k_2} \rceil+1\} max { 0 , ⌊ k 1 y 0 − 1 ⌋ − ⌈ k 2 − x 0 + 1 ⌉ + 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 提高组] 同余方程
同余方程 a x ≡ c ( m o d b ) ax\equiv c \pmod b a x ≡ c ( m o d b ) 其实就是 a x + b y = c ax+by=c a x + b y = c 。答案为 x x x 的最小正整数解。