欧几里德算法(求最大公约数):
LL gcd(LL a, LL b)
{
return b == 0? a : gcd(b, a%b);
}
顺便写下求最小公倍数(lcm)
LL lcm(LL a, LL b)
{
return a*b/gcd(a, b);
}
朴素的欧几里德:
gcd(a, b) = gcd(b, a%b);
扩展欧几里德算法:
LL extend_gcd(LL a, LL b, LL &x, LL &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
else
{
LL r = extend_gcd(b, a%b, y, x);
y -= x*(a/b);
return r;
}
}
该算法一般有三种应用:
- 求解不定方程: ax + by = c (贝祖等式: ax + by = gcd(a, b) )
- 求解模线性方程(线性同余方程)
- 求解模的逆元
应用1:
- 利用它可以求解整数对(x, y).一定存在这样的整数对(x, y), 使得ax + by = gcd(a, b);对于方程ax + by = c 是否有解可以通过判断c是否是gcd(a, b)的倍数, 即gcd(a, b)|c; 如果方程 ax + by = g(令 g = gcd(a, b))的一组解是(x0, y0), 则方程 ax + by = c 有解时, 它的一组解是(x0*c/g, y0*c/g)(gcd(a, b)也表示方程解得个数).
- 利用欧几里德可以较容易求出 ax + by = gcd(a, b)的一组解(x1, y1), 任取另一组解(x2, y2), 则ax1 + by1 = ax2 + by2,因为它们都等于gcd(a, b);移项并合并同类项得 a(x1-x2) = b(y2-y1), 令 gcd(a, b) = g, 等式两边同除以g得, a'(x1-x2) = b'(y2-y1) (a' = a/g, b' = b/g), 此时a'与b'互素(互素:没有共同的质因子或者可以说最大公约数只有1), 由此可知x1 - x2一定是b'的整数倍, 令x1 - x2 = kb', 则y2 - y1 = ka'.
- 不定方程的通解和特解
通解: 通过上面的第二点可知, 设a, b, c为任意整数, 若方程ax + by = c的一组整数解为(x0, y0), 则它的任意整数解都可以写成(x0+kb', y0-ka') (a' = a/gcd(a, b), b' = b/gcd(a, b), k取任意整数);所以(x0+kb', y0-ka')就是不定方程的通解.
特解:利用extend_gcd()可以求得一组解(x, y), 令 ans = extend_gcd(), 特解就等于c*x/ans.
最小解:在实际问题当中,需要的往往是最小整数解,可以通过下面的方法求出最小整数解:
令t = b/gcd(a, b),x是方程a*x + b*y = n的一个特解,则x = c*x/ans, xmin = (x % t + t) % t, y = (c-a*x)/b.
一道采用扩展欧几里德算法的题: poj 1061--青蛙的约会
应用2:
- 模线性方程组: ax ≡ b(mod n) ("≡"为同余号) 含义:"a和b关于模n同余" 或可以说是 "a和b除以n的余数相同"
- 充要条件:a-b是n的整数倍
- 根据模线性方程的定义可知:ax = q*n + r, b = q'*n + r'(a,b,n,q,q',r 都为整数) 由于它们的余数相同即 r = r' 联立等式得, ax - b = n*(q - q') (ax - b是n的整数倍) 令 y = q - q'; 则 ax - ny = b (又回到了不定方程).
LL cal(LL a, LL m, LL c)
{
LL x, y;
LL ans = extend_gcd(a, m, x, y);
if(c%ans==1)
return -1;
x *= c/ans;
m /= ans;
// m = abs(m); //abs只能对int取绝对值
if(m < 0)
m = m*(-1);
LL sum = x%m; //求解最小解
if(sum <= 0)
sum += m; //保证最小解非负
return sum;
}
应用3:
- 当上面的模线性方程组中的 b为1时, 那么就称 ax ≡ 1(mod n) 的解为 a 关于模n的逆(模的逆元),它是模线性方程组的一种特殊式 (也称 x 是 a 关于 m 的乘法逆元,用来求最小解)
- 由上面模线性方程组的转化可知 ax - ny = 1,要想该等式有解,必须使得贝组等式 ax - ny = 1且等式只有唯一解
- 通解:x = x0 + k*(n/gcd(a,n)) 最小解:x = x % n ( gcd(a, n) = 1 ) 其实与上面不定方程求最小解是一样的
//乘法逆元:ax≡1(mod m)
//a对于m的乘法逆元模板
LL cal(LL a, LL m)
{
LL x, y;
LL ans = extend_gcd(a, m, x, y);
if(1%ans==1)
return -1;
x *= 1/ans;
// m = abs(m); //abs只能对int取绝对值
if(m < 0)
m = m*(-1);
LL sum = x%m;
if(sum <= 0)
sum += m;
return sum;
}