文章目录
一、同余式
如果两整数a,b模m的余数相同,则称a,b模m同余,记为a ≡ b(mod m)。如:8 ≡ 2(mod 3)
二、乘法逆元
如果a、b互质,且满足同余方程ax ≡ 1 (mod b),则称x为a模b的乘法逆元,记作a^-1
如:8x ≡ 1(mod 5),解得x = 2,7,12……
三、费马小定理
若p为质数,且a,p互质,则a^(p-1) ≡ 1(mod p),如:4^(3-1) ≡ 1(mod 3)
证明:构造一个与p互质的序列A = { 1, 2, 3, … , p - 1 }。
现在来证明:∏a*Ai ≡ ∏Ai (mod p),i∈[1, p - 1],每个a * Ai(mod p)必独一无二
假设aAi(mod p)与aAj(mod p)的余数都是r,则aAi = xp + r,aAj = yp + r => a(Ai - Aj) = (x - y)p
左边不是p的倍数,而右边是p的倍数,矛盾
从而∏a*Ai mod p就对应了p - 1个不同的余数即A = { 1, 2, 3, … , p - 1 },则∏a*Ai ≡ ∏Ai (mod p)
继而有 a^(p-1) ≡ 1(mod p)
四、裴蜀定理
裴蜀定理又称(贝祖定理)
一定存在整数x,y,满足ax + by = gcd(a, b)。
如:4x + 6y = 2,有整数解x = -1,y = 1,而4x + 6y = 3,x = (3 - 6y) / 4无整数解
证明:
设取整数x0,y0时,ax + by的最小正整数为s,即ax0 + by0 = s,因gcd(a,b) | ax0,gcd(a,b) | by0,故gcd(a,b) | s……(1)
a = qs + r(0 ≤ r < s)
r = a - qs = a - q(ax0 + by0) = a(1 - qx0) + b(-qy0),ax + by的最小正整数为s,故r = 0
则s | a,同理s | b,则s | gcd(a,b)……(2)
故s = gcd(a,b)
4.1裴蜀定理推广一
一定存在整数x,y,满足ax + by = gcd(a,b) × n
如:4x + 6y = 8,有整数解x = -4,y = 4
4.2裴蜀定理推广二
一定存在整数X1……Xi,满足ΣAiXi = gcd(A1,A2……An)
如:4x1 + 6x2 + 2x3 = 2,有整数解x1 = 0,x2 = 0,x3 = 1。
注意
欧几里得算法求gcd(a,b),如果代入负数,结果会返回负数
例: gcd(8,-4) = -4
如果系数Ai为负数,求gcd时可代入其绝对值,确保所求最大公约数为正数,这样并不会影响解的存在。
P4549 【模板】裴蜀定理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define int long long
int n, ans;
int gcd(int a, int b)
{
return !b ? a : gcd(b, a % b);
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 0, a; i < n; i++)
{
cin >> a, ans = gcd(ans, abs(a));
}
cout << ans;
return 0;
}
五、扩展欧几里得算法
问题:求ax + by = gcd(a,b)的一组整数解
- 当b = 0,显然有x = 1,y = 0,这和我们欧几里得算法递归终止条件相同,也是扩欧递归有解的原因
- 当b ≠ 0,由欧几里得算法可知, gcd(a,b) = gcd(b,a%b)
- 由裴蜀定理,gcd(a,b)= ax + by
gcd(b, a%b) = bx1 + (a%b)y1
= bx1 + (a%b)y1
= bx1 + (a - [a/b]*b)y1
= ay1 + b(x1 - (a/b) * y1)
=> x = y1,y = x1 - (a/b) * y1 - 可以用递归算法,先求出下一层的x1,y1,再回代到上一层,层层回代,可求特解(x0,y0)
- 于是有通解:x = x0 + b / gcd(a, b) * k,y = y0 - b / gcd(a, b) * k
- 由裴蜀定理,gcd(a,b)= ax + by
5.1扩欧求二元一次不定方程
问题:求ax + by = c的一组整数解
如果gcd(a, b) | c,那么我们可以用扩欧求出x0,y0,进而求出该方程的解
如果gcd(a, b) ∤ c,那么无解
5.11OJ练习
P5656 【模板】二元一次不定方程 (exgcd) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
跑板子即可
这道题额外要求处理xy的最大最小值,注意细节
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define int long long
int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w *= -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
s = (s << 1) + (s << 3) + (ch ^ 48);
ch = getchar();
}
return s * w;
}
void write(int x)
{
if (x < 0)
x = -x, putchar('-');
if (x > 9)
write(x / 10);
putchar((x % 10) ^ 48);
}
int gcd(int a, int b)
{
return !b ? a : gcd(b, a % b);
}
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int x1, y1, d;
d = exgcd(b, a % b, x1, y1);
x = y1, y = x1 - (a / b) * y1;
return d;
}
signed main()
{
freopen("in.txt", "r", stdin);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int _ = read(), a, b, c, d, x, y, k;
while (_--)
{
a = read(), b = read(), c = read(), d = exgcd(a, b, x, y);
if (c % d)
write(-1);
else
{
x *= c / d, y *= c / d, a /= d, b /= d;
if (x < 0)
k = ceil((1.0 - x) / b), x += k * b, y -= k * a;
else if (x >= 0)
k = (x - 1) / b, x -= b * k, y += a * k;
if (y > 0)
write((y - 1) / a + 1), putchar(' '), write(x), putchar(' '), write((y - 1) % a + 1), putchar(' '), write(x + (y - 1) / a * b), putchar(' '), write(y);
else
write(x), putchar(' '), write(y + a * ceil(1.0 - y / a));
}
putchar('\n');
}
return 0;
}
5.2扩欧求同余方程
给定整数a,b,m,关于x 的同余方程 ax ≡ b(mod m)
如果 x 存在整数解,则输出任意一个
如果不存在,则输出none
我们对同余方程进行转换:ax + my = b
然后进行扩展欧几里得算法即可求出ax + my = gcd(a, m)的特解(注意d ∤ b时无解)
然后x * b / d即为原方程特解
5.21OJ练习
[P1082 NOIP2012 提高组] 同余方程 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
跑板子即可,注意结果可能为负数,所以得加上b再mod b
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define int long long
int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w *= -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
s = (s << 1) + (s << 3) + (ch ^ 48);
ch = getchar();
}
return s * w;
}
void write(int x)
{
if (x < 0)
x = -x, putchar('-');
if (x > 9)
write(x / 10);
putchar((x % 10) ^ 48);
}
int gcd(int a, int b)
{
return !b ? a : gcd(b, a % b);
}
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int x1, y1, d;
d = exgcd(b, a % b, x1, y1);
x = y1, y = x1 - (a / b) * y1;
return d;
}
signed main()
{
// freopen("in.txt", "r", stdin);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int a = read(), b = read(), x, y;
int d = exgcd(a, b, x, y);
// ax+by =1
write((x + b) % b);
return 0;
}