线性同余方程
欧几里得算法
原理
欧几里得算法用于求解两个数的最大公约数,它基于下面这个公式:
可以简单地证明:
设 \(A\) 为 \(a,b\) 的公约数组成的集合, \(B\) 为 \(b,a\bmod b\) 的公约数组成的集合,只需证 \(\max\{A\}=\max\{B\}\)
-
若 \(m\in A\) ,则 \(\exist k_1,k_2\text{ s.t. }m\times k_1=a,m\times k_2=b\)
而 \(a \bmod b=a-b\lfloor\frac{a}{b}\rfloor=m(k1-k_2\lfloor\frac{k_1}{k_2}\rfloor)\)
所以 \(m\in B\)
-
若 \(m\in B\) ,则 \(\exist k_1,k_2\text{ s.t. }m\times k_1=b,m\times k_2=a \bmod b\)
而 \(a=a\bmod b+b\lfloor\frac{a}{b}\rfloor=m(k_2+k_1\lfloor\frac{a}{b}\rfloor)\)
所以 \(m\in A\)
综上, \(A=B,\max\{A\}=\max\{B\}\)
模板
int gcd(int a, int b)
{
return a % b == 0 ? b : gcd(b, a % b);
}
扩展欧几里得算法
原理
扩展欧几里得算法在求出最大公约数的的同时还能求出不定方程 \(ax+by=\gcd(a,b)\) 的一组解
可以推出:
所以有:
所以只要求出 \(x_2,y_2\) 就能得到 \(x_1,y_1\)
模板
int exgcd(int a, int b, int & x, int & y)
{
if(b == 0) {
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - y * a / b;
return d;
}
还有一种更简洁的版本,在递归调用时直接交换 \(x,y\)
int exgcd(int a, int b, int & x, int & y)
{
if(b == 0) {
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
线性同余方程
线性同余方程指的是形如 \(ax\equiv c\pmod b\) 的方程,它等价于方程 \(ax+by=c\) ,有解的充要条件为:
所以可以先用扩展欧几里得算法求出 \(ax+by=\gcd(a,b)\) 的一组解 \((x_0,y_0)\) ,那么原方程的一组解就是:
设 \(a_1\gcd(a,b)=a,b_1\gcd(a,b)=b\) ,则通解可以表示为:
其中 \(t\in\mathbb{Z}\) ,由 \(a_1 b=ab_1\) ,将通解带回原式,很容易就能验证其正确性
最小的正整数解为:
\(x,y\) 都为正时,有如下不等式:
所以 \(x,y\) 都为正的解的个数为:
若此公式得出的结果不是正数则说明没有 \(x,y\) 都为正的解
模板
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll exgcd(ll a, ll b, ll & x, ll & y)
{
if(b == 0) {
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int t;
scanf("%d", &t);
while(t--) {
ll a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
ll d, x, y;
d = exgcd(a, b, x, y);
if(c % d != 0) {
printf("-1\n");
continue;
}
x = x * c / d;
y = y * c / d;
ll a1 = a / d, b1 = b / d;
ll t1 = ceil((double)(-x + 1) / b1);
ll t2 = floor((double)(y - 1) / a1);
if(t1 > t2) {
ll ansx = x + t1 * b1;
ll ansy = y - t2 * a1;
printf("%lld %lld\n", ansx, ansy);
} else {
int cnt = t2 - t1 + 1;
ll minx = x + t1 * b1;
ll maxx = x + t2 * b1;
ll miny = y - t2 * a1;
ll maxy = y - t1 * a1;
printf("%d %lld %lld %lld %lld\n", cnt, minx, miny, maxx, maxy);
}
}
return 0;
}