前置知识
数论基本概念,欧几里得算法
裴蜀定理
在学习exgcd之前,我们需要先学习一下裴蜀定理。
裴蜀定理有两条:
- 对于任意的整数 a , b a,b a,b,存在一组整数 x , y x,y x,y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
- 若 a , b a,b a,b 是整数,那么对于任意的整数 x , y x,y x,y,都有 g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)∣ax+by
第一条比第二条难证,我们先来证明第一条,下面是证明过程,我尽量讲的通俗易懂:
我们考虑辗转相除的最后一步,此时 b = 0 , a = g c d ( a , b ) b=0,a=gcd(a,b) b=0,a=gcd(a,b),所以 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 一定有解( 解为 x = 1 , y = 0 x=1,y=0 x=1,y=0 )。
而辗转相除这一步的 a a a 为上一步的 b b b,这一步的 b b b 为上一步的 a m o d b a \bmod b amodb,所以我们代进去,得: b x + ( a m o d b ) y = g c d ( b , a m o d b ) bx+(a \bmod b)y=gcd(b,a\bmod b) bx+(amodb)y=gcd(b,amodb)
因为最后一步的
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b) 有解,所以
b
x
+
(
a
m
o
d
b
)
y
=
g
c
d
(
b
,
a
m
o
d
b
)
bx+(a \bmod b)y=gcd(b,a\bmod b)
bx+(amodb)y=gcd(b,amodb) 必定有解。我们设它的解为
{
x
=
x
′
y
=
y
′
\begin{cases}x=x'\\y=y'\end{cases}
{x=x′y=y′,则有:
b
x
′
+
(
a
m
o
d
b
)
y
′
=
g
c
d
(
b
,
a
m
o
d
b
)
bx'+(a \bmod b)y'=gcd(b,a\bmod b)
bx′+(amodb)y′=gcd(b,amodb)
而我们知道:
g
c
d
(
b
,
a
m
o
d
b
)
=
g
c
d
(
a
,
b
)
gcd(b,a\bmod b)=gcd(a,b)
gcd(b,amodb)=gcd(a,b)
a
m
o
d
b
=
a
−
⌊
a
b
⌋
b
a \bmod b=a-\left\lfloor\dfrac{a}{b}\right\rfloor b
amodb=a−⌊ba⌋b
所以可得:
b
x
′
+
(
a
−
⌊
a
b
⌋
b
)
y
′
=
g
c
d
(
a
,
b
)
bx'+(a-\left\lfloor\dfrac{a}{b}\right\rfloor b)y'=gcd(a,b)
bx′+(a−⌊ba⌋b)y′=gcd(a,b)
转化一下:
a
y
′
+
b
x
′
−
y
′
⌊
a
b
⌋
b
=
g
c
d
(
a
,
b
)
ay'+bx'-y'\left\lfloor\dfrac{a}{b}\right\rfloor b=gcd(a,b)
ay′+bx′−y′⌊ba⌋b=gcd(a,b)
a
y
′
+
b
(
x
′
−
y
′
⌊
a
b
⌋
)
=
g
c
d
(
a
,
b
)
ay'+b(x'-y'\left\lfloor\dfrac{a}{b}\right\rfloor)=gcd(a,b)
ay′+b(x′−y′⌊ba⌋)=gcd(a,b)
我们发现,式子的左边成为了 a x + b y ax+by ax+by 的形式,所以我们得到,方程 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 的解为: { x = y ′ y = x ′ − y ′ ⌊ a b ⌋ \begin{cases}x=y'\\y=x'-y'\left\lfloor\dfrac{a}{b}\right\rfloor\end{cases} {x=y′y=x′−y′⌊ba⌋
这样,对于任何的 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),我们都可以通过这种方法证得有解(数学归纳法)。
下面我们来证明第二条:
我们设
d
=
g
c
d
(
a
,
b
)
d=gcd(a,b)
d=gcd(a,b),则有
d
∣
a
,
d
∣
b
d|a,d|b
d∣a,d∣b
因为
x
,
y
x,y
x,y 均为整数,所以
d
∣
a
x
,
d
∣
b
y
d|ax,d|by
d∣ax,d∣by
所以得
d
∣
a
x
+
b
y
d|ax+by
d∣ax+by
即
g
c
d
(
a
,
b
)
∣
a
x
+
b
y
gcd(a,b)|ax+by
gcd(a,b)∣ax+by
得证。
这样,我们就证明了裴蜀定理。我们来看一道例题:洛谷 P4549 【模板】裴蜀定理。
这个题目主要运用了裴蜀定理的第二条:若 a , b a,b a,b 是整数,那么对于任意的整数 x , y x,y x,y,都有 g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)∣ax+by。那么,我们得到, g c d ( a , b ) gcd(a,b) gcd(a,b) 是 a x + b y ax+by ax+by 的最小正整数值。
推广到多个形如 A i X i A_iX_i AiXi 的单项式上, ∑ i = 1 n A i X i \sum\limits^n_{i=1}A_iX_i i=1∑nAiXi 的最小正整数值为 g c d { A i } gcd\{A_i\} gcd{Ai},也就是题目中的 S S S。
注意输入中有负数,取个绝对值就行了。
C o d e Code Code
#include<iostream>
#include<cstdio>
using namespace std;
int n;
int a[30];
int ans;
int gcd(int x,int y)
{
if(y==0)
return x;
return gcd(y,x%y);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
if(a[i]<0)
a[i]=-a[i];
ans=gcd(ans,a[i]);
}
printf("%d",ans);
return 0;
}
扩展欧几里得算法
扩展欧几里得算法(exgcd),正如名字一样,建立在欧几里得算法的基础上。事实上,它还依赖于上述的裴蜀定理。
exgcd是干什么用的呢?我们看到裴蜀定理的第一条:
对于任意的整数 a , b a,b a,b,存在一组整数 x , y x,y x,y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
exgcd 就是求 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 的解。
仔细阅读上述裴蜀定理第一条的证明过程, a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 的解是什么?
{ x = y ′ y = x ′ − y ′ ⌊ a b ⌋ \begin{cases}x=y'\\y=x'-y'\left\lfloor\dfrac{a}{b}\right\rfloor\end{cases} {x=y′y=x′−y′⌊ba⌋
那么 x ′ x' x′ 和 y ′ y' y′ 又是怎么求出来的呢?它们是方程 b x + ( a m o d b ) y = g c d ( b , a m o d b ) bx+(a \bmod b)y=gcd(b,a\bmod b) bx+(amodb)y=gcd(b,amodb) 的解。
可以发现,这是一个递归的过程,不断的递归求出 x ′ x' x′ 和 y ′ y' y′,从而得到 x x x 和 y y y。
那么递归的边界又是什么呢?当 b = 0 b=0 b=0 时,解为 { x = 1 y = 0 \begin{cases}x=1\\y=0\end{cases} {x=1y=0 。
int x,y;
void exgcd(int a,int b)
{
if(b==0)
{
x=1;
y=0;
return;
}
exgcd(b,a%b);
int t=x;//暂存x
x=y;
y=t-a/b*y;//注意不能写成y*a/b,必须先计算a/b
}
来道例题:洛谷 P1082 [NOIP2012 提高组] 同余方程
我们转换一个形式:
a
x
m
o
d
b
=
1
ax \bmod b=1
axmodb=1
因为我们知道,
a
m
o
d
b
=
a
−
⌊
a
b
⌋
×
b
a\bmod b=a-\left\lfloor\dfrac{a}{b}\right\rfloor\times b
amodb=a−⌊ba⌋×b,所以得:
a
x
−
⌊
a
x
b
⌋
×
b
=
1
ax-\left\lfloor\dfrac{ax}{b}\right\rfloor\times b=1
ax−⌊bax⌋×b=1
稍微改动,它成了这个形式:
a
x
+
b
y
=
1
ax+by=1
ax+by=1
因为题目告诉我们 a x m o d b = 1 ax \bmod b=1 axmodb=1 有解,所以 a x + b y = 1 ax+by=1 ax+by=1 有解。
裴蜀定理第二条告诉我们, g c d ( a , b ) ∣ a x + b y gcd(a,b)|ax+by gcd(a,b)∣ax+by,而 a x + b y = 1 ax+by=1 ax+by=1,则 g c d ( a , b ) ∣ 1 gcd(a,b)|1 gcd(a,b)∣1,那么 g c d ( a , b ) gcd(a,b) gcd(a,b) 只能等于 1 1 1。
把
1
1
1 代换一下,方程又成了:
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)
可以使用exgcd求解。
题目要求的是最小正整数解,而exgcd求出的可能更大,也可能是负数。那我们怎么把它转化为最小正整数解呢?
我们可以证明,所有的解之间的差均为 b b b,证明过程如下:
我们知道 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),即 g c d ( a , b ) − a x b = y \dfrac{gcd(a,b)-ax}{b}=y bgcd(a,b)−ax=y,因为 y y y 是整数,所以 b ∣ g c d ( a , b ) − a x b|gcd(a,b)-ax b∣gcd(a,b)−ax。
现在我们设 x x x 增加了 Δ x \Delta x Δx,则原式变为 g c d ( a , b ) − a x − a Δ x gcd(a,b)-ax-a\Delta x gcd(a,b)−ax−aΔx。
而因为 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1 即 a , b a,b a,b 互质,则 b ∣ Δ x b|\Delta x b∣Δx,得证。
那么,我们就可以通过加减 b b b 的方式找到答案。具体做法么,直接模 b b b 就行了……
C o d e Code Code
#include<iostream>
#include<cstdio>
using namespace std;
long long a,b;
long long x,y;
void exgcd(long long a,long long b)
{
if(b==0)
{
x=1;
y=0;
return;
}
exgcd(b,a%b);
long long t=x;
x=y;
y=t-a/b*y;
}
int main()
{
scanf("%lld%lld",&a,&b);
exgcd(a,b);
printf("%lld",(x%b+b)%b);
return 0;
}