This commit is contained in:
HuangHai
2025-08-30 18:35:01 +08:00
commit 1f397eca87
6258 changed files with 1487241 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// P1082 [NOIP2012 提高组] 同余方程
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
LL a, b;
scanf("%lld %lld", &a, &b);
LL x, y;
exgcd(a, b, x, y);
printf("%d\n", (x % b + b) % b);
return 0;
}

View File

@@ -0,0 +1,64 @@
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100009;
//洛谷P4777 【模板】扩展中国剩余定理(EXCRT)
LL n; // n组数据
LL mod[N], yu[N]; //每组测试数据中,模记为mod[i],余数记为yu[i]
//龟速乘
LL qmul(LL a, LL k, LL b) {
LL res = 0;
while (k) {
if (k & 1) res = (res + a) % b;
a = (a + a) % b;
k >>= 1;
}
return res;
}
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
//最小公倍数
LL lcm(LL a, LL b) {
LL x, y;
LL d = exgcd(a, b, x, y);
return (a / d * b);
}
//扩展中国剩余定理
LL excrt() {
LL M = mod[1], ans = yu[1];
// M为前i-1个数的最小公倍数,即lcm(mod[1],mod[2],...,mod[n])
// ans为前i-1个方程的通解
LL x, y;
for (int i = 2; i <= n; i++) {
LL m = mod[i]; //当前要合并方程的模
LL res = (yu[i] - ans % m + m) % m; // r2-r1,之所以%mi,是为了防止余数差出现负数
LL d = exgcd(M, m, x, y); // 1、求特解x0; 2、求最大公约数d
if (res % d) return -1; //无解的情况
LL t = qmul(x, res / d, m); //根据ax+by=1的特解翻番得到方程ax+by=c的特解
ans += t * M; //获得前i个方程的通解 r=r + x*lcm(m1,m2,..)
M = lcm(M, m); //更改M的值,把mi也加到M中去,相当于不断的取所有mi的最小公倍数
ans = (ans % M + M) % M; //对最小公倍数取模就是最小的合理解
}
return ans;
}
int main() {
ios::sync_with_stdio(false); //加速cin和cout
cin >> n;
for (int i = 1; i <= n; i++) cin >> mod[i] >> yu[i];
cout << excrt() << endl;
return 0;
}

View File

@@ -0,0 +1,84 @@
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
/*
测试用例:
1
2 3 5
答案:
1 1 1 1 1
*/
int main() {
int t;
scanf("%d", &t);
while (t--) {
LL a, b, c;
scanf("%lld %lld %lld", &a, &b, &c);
LL x, y;
LL d = __gcd(a, b); //使用库函数__gcd计算最大公约数d
if (c % d) //裴蜀定理,当gcd(a,b)不能整除c时,方程无解,输出-1
puts("-1");
else {
//这步很值得总结:一个方程拿到手,第一步是化简,就是把a,b,c中都除以gcd(a,b),实现化简
//举个栗子:2x+4y=8 -> x+2y=4 很直白吧,套路吧~ 此方程与原方程的角系一样吧~
//当然,也可能化简不了,比如: 2x+3y=5,因为gcd(2,3)=1,化不化简都一样
//但这里有一个事实,因为 a'=a/gcd(a,b),b'=b/gcd(a,b)后,a',b'肯定是互质。
//令d'=gcd(a',b') 现在d'=1
a /= d, b /= d, c /= d;
//扩展欧几里得,一通用形式返回d,在有的题解中,不采用exgcd返回d,而是用__gcd计算d.
//把扩展欧几里得算法定位为取特解x,y专用,也是比较直观简便的。
//性能上嘛,由于辗转相除法不断的变更除数,速度上很快,两次也没有问题。
//扩欧的形式:ax+by=gcd(a,b),求方程的一组解 x,y。现在的情况下,gcd(a,b)=1了,可以
exgcd(a, b, x, y);
x *= c, y *= c; //结果(x,y)需要乘以c才能还原为ax+by=c的方程解。
/*
通解公式: x=x0+b/d*t
y=y0-a/d*t
此时d=1,则通解公式:
x=x0+b*t
y=y0-a*t
想要xmin => x0固定值,b固定值,只能变化的是t,每变化一次就增加、减少一个b
*/
// printf("x=%lld y=%lld a=%lld b=%lld\n", x, y, a, b);
LL xmin = (x % b + b) % b; //利用最小正整数解的公式(需要特判0)
if (xmin == 0) xmin = b; // xmin如果是0,需要特判,加一个b搞定~
LL ymin = (y % a + a) % a; //利用最小正整数解的公式(需要特判0)
if (ymin == 0) ymin = a; // ymin如果是0,需要特判,加一个a搞定~
// printf("xmin=%lld ymin=%lld\n", xmin, ymin);
// x最大值怎么求呢?简单利用原始方程变形求
// ax+by=c => ax=c-by => x = (c-by)/a
// x要想最大,则y必须取最小值
LL xmax = (c - b * ymin) / a;
// y要想最大,则x必须取最小值
LL ymax = (c - a * xmin) / b;
if (xmax <= 0 || ymax <= 0) //若最大值还不是正整数,则无正整数解
printf("%lld %lld\n", xmin, ymin); //输出最小值
else {
//解的个数是最大 x 最小 x 的差除以模数 b 再加上1。
LL sum = (xmax - xmin) / b + 1; //不足b时,也有1个解,+b就增加一个解
printf("%lld %lld %lld %lld %lld\n", sum, xmin, ymin, xmax, ymax);
}
}
}
return 0;
}

View File

@@ -0,0 +1,27 @@
算法专题——整除同余(数论汇总)
https://www.cnblogs.com/TanJI-C/p/15842256.html
### 四、其它同学的学习感言
中国剩余定理-----》拓展欧几里得是啥来??----》终于看懂exgcd啦-----》除法逆元咋求来??----》由费马小定理易知a%m的逆元是a^(m-2)%m----》费马小定理是啥来-----》啊~~~
终于到头啦,自己实在是太菜啦
不过也看清楚一个道理,知识都是相连的,学好一个知识点不要怕累,都怪自己太懒就行啦
### 五、费马小定理【模板例题】
https://www.cnblogs.com/1203ljh/p/4663460.html
HDU4869:Turn the pokers(费马小定理+快速幂)
HDU 4704
TODO 待功能加深后再仔细研究
https://blog.csdn.net/Zhengyangxinn/article/details/108824537
牛客竞赛,登录采用的是qq登录,手机号用的是18686619970。
[牛牛和牛可乐的赌约](https://ac.nowcoder.com/acm/contest/7412/A)
https://blog.csdn.net/qq_26139541/article/details/108743380
[抽卡](https://ac.nowcoder.com/acm/contest/5600/D)
https://blog.csdn.net/HenryYang2018/article/details/89399986

View File

@@ -0,0 +1,9 @@
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
int main() {
printf("%d\n", 729 % 13);
return 0;
}

View File

@@ -0,0 +1,216 @@
### 中国剩余定理(CRT)
[原文链接](https://zhuanlan.zhihu.com/p/44591114)
#### 问题背景
孙子定理是中国古代求解一次同余式方程组的方法。是数论中一个重要定理。又称中国余数定理。一元线性同余方程组问题最早可见于中国南北朝时期(公元$5$世纪)的数学著作《孙子算经》卷下第二十六题,叫做“**物不知数**”问题,原文如下:
  有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?即,一个整数除以三余二,除以五余三,除以七余二,求这个整数。《孙子算经》中首次提到了同余方程组问题,以及以上具体问题的解法,因此在中文数学文献中也会将中国剩余定理称为孙子定理。
用现代数学的语言来分析这个问题,中国剩余定理给出了以下的一元线性同余方程组:
$$
\large \left\{\begin{matrix}
x \equiv a_1 (mod \ m_1) \\
x \equiv a_2 (mod \ m_2) \\
... \\
x \equiv a_n (mod \ m_n)
\end{matrix}\right.
$$
* 在中国剩余定理(以下简称 $CRT$ ) 中$m_1,m_2,...,m_n$为**两两互质**的整数
<br>
* 在扩展中国剩余定理(以下简称 $EXCRT$)中$m_1,m_2,...,m_n$并不满足互质条件,后者相对于前者更难解决。而两者都要运用一个算法来解决:**扩展欧几里得算法**。
下面的问题证明牵涉到一个 <font color='red'><b>最基本数学定理</b></font> ,如果有`a%b=c`,则有`(a+kb)%b=c`(`k`为非零整数),换句话说,如果一个除法运算的余数为`c`,那么被除数与`k`倍的除数相加(或相减)的和(差)再与除数相除,余数不变。这个是很好证明的。
问题1:计算一个整数 $x$ ,使得它满足除以$3$余$2$、除以$5$余$3$、除以$7$余$2$。
如果能够找到三个整数 $x_1,x_2,x_3$ ,使得:
$x_1$ 除以$3$余$2$、除以$5$余$0$、除以$7$余$0$;
$x_2$ 除以$3$余$0$、除以$5$余$3$、除以$7$余$0$;
$x_3$ 除以$3$余$0$、除以$5$余$0$、除以$7$余$2$;
那么令 $x=x_1+x_2+x_3$,利用上面的基本定理,我们知道这时的 $x$ 就满足除以$3$余$2$、除以$5$余$3$、除以$7$余$2$。
分别称找到整数 $x_1,x_2,x_3$ 的问题为**问题1-1**、**问题1-2**、**问题1-3**。可以看出这三个问题本质上是类似的。
下面对**问题1-1**继续分解,如果能够找到一个整数 $y_1$ 满足 $y_1$ 除以$3$余$1$、除以$5$余$0$、除以$7$余$0$,那么令 $x_1=2\times y_1$ ,就很容易验证这时的 $x_1$ 就满足除以$3$余$2$、除以$5$余$0$、除以$7$余$0$。
因此定义
**问题1-1-1**为:寻找整数 $y_1$ 满足 $y_1$ 除以$3$余$1$、除以$5$余$0$、除以$7$余$0$;
**问题1-2-1**为:寻找整数 $y_2$ 满足 $y_2$ 除以$3$余$0$、除以$5$余$1$、除以$7$余$0$;
**问题1-3-1**为:寻找整数 $y_3$ 满足 $y_3$ 除以$3$余$0$、除以$5$余$0$、除以$7$余$1$。
这三个问题本质上是相同的。
如果找到了 $y_1,y_2,y_3$ ,那么就可以取 $x=2\times y_1 + 3 \times y_2 + 2 \times y_3$。
---
下面就以**问题1-1-1**为例:寻找整数 $z$ 使得 $z$ 除以$3$余$1$、除以$5$余$0$、除以$7$余$0$。
于是 $z$ <font color='red'><b>一定</b></font> 是$5 \times 7=35$的倍数,假设$z=35k$。
那么就有$35k \equiv 1 (mod \ 3)$,而这时的 <font color='red'><b>就是$5 \times 7$模$3$的逆元$k$</b></font> 。这样,就转化为以前的知识,可以通过扩展欧几里得来求逆元,也就是求得了$k \Rightarrow z \Rightarrow y \Rightarrow x \Rightarrow $最终的解。
将这个$k$记作$[35^{-1}]_3$,那么$z$就等于$5 \times 7 \times [(5\times 7)^{-1}]_3$,恰好就是 $5 \times 7 \times 2=70$,对应“凡三三数之剩一,则置七十”一句及“三人同行七十稀”一句。
于是类推得到,
**问题1-1-2**的解答是 $3\times 7 \times [(3 \times 7)^{-1}]_5$ ,恰好就是 $3 \times 7 \times 1=21$ ,对应“五五数之剩一,则置二十一”一句及“五树梅花廿一枝”一句;
**问题1-1-3**的解答是 $3 \times 5 \times [(3\times 5)^{-1}]_7$ ,恰好就是 $3\times 5\times 1=15$ ,对应“七七数之剩一,则置十五”一句及“七子团圆月正半”一句。
所以将分解的问题复原,可得:
$$x=2\times (5 \times 7 \times [(5\times 7)^{-1}]_3)+3\times (3\times 7 \times [(3\times 7)^{-1}]_5)+2\times (3\times 5 \times [(3 \times 5)^{-1}]_7)$$。
。
最后 ,注意到,如果 $x$ 满足除以$3$余$2$、除以$5$余$3$、除以$7$余$2$,那么 $x+3\times 5 \times 7$ 也同样满足。
因此要计算满足要求的最小的非负整数,就只需要**计算总和除以$105$的余数**即可。——对应“除百零五便得知”一句。
---
下面要讨论的是:如果有多个满足要求的整数,那么**它们之间有什么关系**呢?
假设 $X,Y$ 都满足“除以$3$余$a$、除以$5$余$b$、除以$7$余$c$”。
观察 $X-Y$ 会发现, $X-Y$ 满足“除以$3$余$0$、除以$5$余$0$、除以$7$余$0$”。因此 $X-Y$一定是 $3\times 5 \times 7$ 的倍数。
这也就是说,在“模$105$同余”的意义下,之前通过分解问题、组合解答的方法所得到的 $x$ 恰恰就是唯一解。
---
下面把这个问题一般化:假设整数$m_1,m_2,m_3,...,m_n$两两互素,则对于任意的整数 $a_1,a_2,a_3,...,a_n$ ,方程组
$\large
\left\{\begin{matrix}
x \equiv a_1 (mod \ m_1) \\
x \equiv a_2 (mod \ m_2) \\
... \\
x \equiv a_n (mod \ m_n) \\
\end{matrix}\right.
$
都存在整数解,且若$X,Y$都满足该方程组,则必有 $\large \displaystyle X \equiv Y \ (mod \ M)$ ,其中 $\large \displaystyle M=\prod _{i=1}^{n}m_i$。
具体而言,
$$\large \displaystyle x \equiv \sum_{i=1}^{n}a_i \times \frac{M}{m_i} \times \begin{bmatrix}
(\frac{M}{m_i})^{-1}
\end{bmatrix}
_{m_i} \ (mod \ M)$$ 。
——这就是我非常看重和喜欢的中国剩余定理(Chinese remainder theorem, CRT)。
例题:
[P3868 [TJOI2009] 猜数字](https://www.luogu.com.cn/problem/P3868)
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 20;
int n;
LL a[N], m[N];
LL M = 1, ans;
LL x, y;
//龟速乘
LL qmul(LL a, LL k, LL b) {
LL res = 0;
while (k) {
if (k & 1) res = (res + a) % b;
a = (a + a) % b;
k >>= 1;
}
return res;
}
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 1; i <= n; i++) scanf("%lld", &m[i]), M *= m[i]; // M是累乘积
// a[i]可能为负数,需要预处理
for (int i = 1; i <= n; i++) a[i] = (a[i] % m[i] + m[i]) % m[i];
//中国剩余定理
for (int i = 1; i <= n; i++) {
exgcd(M / m[i], m[i], x, y); //求逆元x
x = (x % m[i] + m[i]) % m[i]; //防负数
ans = (ans + qmul(a[i], M / m[i] * x, M)) % M; //调用龟速乘
}
printf("%lld\n", ans);
return 0;
}
```
[P1495 【模板】中国剩余定理(CRT)/曹冲养猪](https://www.luogu.com.cn/problem/P1495)
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 20;
//曹冲养猪 洛谷 P1495
//曹冲养猪 AcWing 1298
int n;
LL a[N], m[N];
LL M = 1, x, y, ans;
//龟速乘
LL qmul(LL a, LL k, LL b) {
LL res = 0;
while (k) {
if (k & 1) res = (res + a) % b;
a = (a + a) % b;
k >>= 1;
}
return res;
}
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
scanf("%d", &n); //建立猪圈的次数
// m[1]=3 第1次建立3个猪圈
// a[1]=1 第1次有1只猪无家可归,也就是余数
// 也就是 x % 3 = 1
for (int i = 1; i <= n; i++) scanf("%lld%lld", &m[i], &a[i]), M *= m[i];
// a[i]可能为负数,需要预处理
// for (int i = 1; i <= n; i++) a[i] = (a[i] % m[i] + m[i]) % m[i];
for (int i = 1; i <= n; i++) {
exgcd(M / m[i], m[i], x, y); //求逆元x
x = (x % m[i] + m[i]) % m[i]; //防负数
ans = (ans + qmul(a[i], M / m[i] * x, M)) % M; //调用龟速乘
}
printf("%lld\n", ans);
return 0;
}
```
参考题解:https://www.acwing.com/solution/content/74377/

View File

@@ -0,0 +1,50 @@
## 初等数论--同余方程--二元一次不定方程的通解形式
* 不定方程:变量个数>方程个数
若二元一次不定方程$ax + by = n$有解,$x_0, y_0$为它的一组整数解,则通解为:
$$\large
\left\{\begin{matrix}
x=x_0 + \frac{b}{(a,b)} \cdot t\\
y=y_0-\frac{a}{(a,b)}\cdot t
\end{matrix}\right.
\ \ \ \ t \in Z
$$
​
证明:
* **该形式确实是二元一次方程的解**
将$x,y$代入原方程,得:
$\large \displaystyle a(x_0+\frac{b}{(a,b)}\cdot t) + b(y_0-\frac{a}{(a,b)}\cdot t)$
$\large \displaystyle =ax_0+a\frac{b}{(a,b)}\cdot t + by_0-b\frac{a}{(a,b)}\cdot t$
$\large \displaystyle =ax_0+by_0$
$\large \displaystyle =n$
* **二元一次不定方程的解都可以表达成这种形式**
已知
$$\large
\left\{\begin{matrix}
ax+by=n
\\
ax_0+by_0=n
\end{matrix}\right.
$$
联立方程,相减得:
$$\large a(x-x_0)+b(y-y_0)=0$$
$$\large a(x-x_0)=-b(y-y_0)$$
$$\large \frac{a}{(a,b)}(x-x_0)=-\frac{b}{(a,b)}(y-y_0)$$
$$\large \because \frac{a}{(a,b)}\nmid \frac{b}{(a,b)}$$
且
$$\large \frac{b}{(a,b)} | \frac{a}{(a,b)}(x-x_0)$$
$$\large \therefore \frac{b}{(a,b)} | x-x_0$$
即
$$\large x-x_0=\frac{b}{(a,b)}\cdot t$$
同理,$\large \displaystyle \frac{a}{(a,b)}|y-y_0$,即
$$\large y-y_0=-\frac{a}{(a,b)}\cdot t$$
$$\huge Q.E.D$$

View File

@@ -0,0 +1,72 @@
https://blog.csdn.net/sleepiest/article/details/81865741
这个不看懂,没法看
https://www.luogu.com.cn/blog/blue/kuo-zhan-zhong-guo-sheng-yu-ding-li
### 1 导语
同余方程组,是形如
$$\large x \equiv x_1 (mod \ \ p_1 ) \\
\large x \equiv x_2 (mod \ \ p_2 ) \\
\large ... \\
\large x \equiv x_n (mod \ \ p_n )$$
的方程组。
当$\large p_1,p_2,...,p_n$两两互质的时候,我们可以使用中国剩余定理。但如果他们不互质呢?
### 2 裴蜀定理与扩展欧几里得算法
这里先介绍一下**裴蜀定理与扩展欧几里得算法**作为**铺垫**。
裴蜀定理:对于不完全为 的非负整数 , 表示 的最大公约数。则必然存在整数对 ,使得
$$\large (a,b)=ax+by$$
具体证明略。
扩展欧几里得算法则是对这个方程求解的算法。
### 3 合并同余方程组
比如我们先合并前两个方程。
$$\large x \equiv x_1 (mod \ \ p_1 ) \\ \large x \equiv x_2 (mod \ \ p_2 )$$
根据余数的定义,我们可以将方程改写为
$$\large x=x_1 \times k_1 +p_1 \ \ (1) \\
x=x_2 \times k_2 +p_2 \ \ (2)
$$
合并$(1),(2)$得:
$$\large x_1*k_1-x_2*k_2=p_2-p_1 \ \ (3) $$
这是一个关于$k_1,k_2$的不定方程,当$\large (p_2—p_1)\% gcd(x_1,x_2)=0$时有解。
当有解时,用扩展欧几里得可求得 $k_{1}$ 的一个特解,设为 $k^{'}$ ,
那么 $k_{1}$ 的通解为:$\large k_{1} = k^{'}+ t * (x_{2} / gcd(x_{1},x_{2}))$
将 $k_{1}$ 代回到式$1$中可得:
$$\large x_1*(k^{'}+t*(x_2/gcd(x_1,x_2)))+p_1=x \\
\Rightarrow \\
\large x_1*x_2 /gcd(x_1,x_2)*t+x_1*k^{'}+p_1= x $$
可以发现:$\large x_{1} *x_{2} / gcd(x_{1},x_{2})$是$x_{1} ,x_{2}$ 的最小公倍数,可以转换表示为 $[x_{1},x_{2}]$
$$\large [x_1,x_2]*t+x_1*k^{'}+p_1=x $$
所以合并式$(1),(2)$两个同余组,变为:
$$\large x ≡ x_{1} * k^{'} + p_{1} (mod [x_{1},x_{2}])$$
$\large x_{1}, p_{1}$ 都是已知的,只需求某个特解 $\large k^{'}$ 和 $\large [x_{1},x_{2}]$ 即可。
### 4 举个栗子
$$ \large
\left\{\begin{matrix}
x \equiv 2(mod \ 4) \\
x \equiv 4(mod \ 6) \\
\end{matrix}\right. \Rightarrow x \equiv 10 (mod \ 12)
$$
由$(1)$式,手动模拟一些$x$,得:$6,$**10**$,14,18,$**22**$,26,30,$**34**$,38,42,$**46**$...$
由$(2)$式,手动模拟一些$x$,得:**10**$,16,$**22**$,28,$**34**$,40,$**46**$,...$
可以发现,这两个式子要想得到的公共解,其实是一组解,它们是:$\large 10,22,34,46,...$
找一下规律,就是从$\large 10$开始,循环节为$\large 12$,也就是原来两个模数$\large p_1,p_2$的$\large [p_1,p_2]$。

View File

@@ -0,0 +1,160 @@
## 一、思想
中国剩余定理($CRT$)**不能解决模数不互质情况的模线性同余方程组**。
方程组是由很多个模线性同余方程构成的。假设我们能把两个模线性同余方程组,等价地合并成一个方程,问题就迎刃而解了——只需要不停地合并这些方程,只到只剩下一个。
理想是美好的,道路是曲折的。首先,未必两个方程可以合并成一个方程(我们可能找不到快速的实现方式)。此外,即使可以把两个方程合并成一个,这个变换也未必是等价变换(可能新方程的解未必是原方程的解,也可能原方程的解被新方程漏掉了)。我们要做的事情是:给出将两个方程合并成一个方程的方法,然后证明这个变换的等价性。
---
### 二、数学基础
 作为我们接下来讨论的基础,我想先展示几个例子。它们的规模都很小,完全可以手工验证。
$$ \large
\left\{\begin{matrix}
x \equiv 2(mod \ 4) \\
x \equiv 4(mod \ 6) \\
\end{matrix}\right. \Rightarrow x \equiv 10 (mod \ 12)
$$
由$(1)$式,手动模拟一些$x$,得:$6, 10,14,18,22,26,30,34,38,42,46...$
由$(2)$式,手动模拟一些$x$,得:$10,16,22,28,34,40,46,52,58...$
可以发现,这两个式子要想得到的公共解,其实是一组解,它们是:$\large 10,22,34,46,...$
找一下规律,就是从$\large 10$开始,循环节为$\large 12$,也就是原来两个模数$\large 4,6$的$\large [4,6]$。
---
$$ \large
\left\{\begin{matrix}
x \equiv 4(mod \ 6) \\
x \equiv 3(mod \ 5) \\
\end{matrix}\right. \Rightarrow x \equiv 28 (mod \ 30)
$$
$\large (1): 10,16,22,28,34,40,46,52,58,64,70,76,...$
$\large (2): 8,13,18,23,28,33,38,43,48,53,58,63,68,73,78,...$
总结规律:循环节是$30$,最小起点是$28$
---
$$ \large
\left\{\begin{matrix}
x \equiv 2(mod \ 4) \\
x \equiv 3(mod \ 6) \\
\end{matrix}\right. \Rightarrow ∅
$$
$\large (1):2,6,10,14,18,22,26,30,34,38,42,....$
$\large (2):3,9,15,21,27,33,39,...$
可以发现,两者完美错过(~~一个是奇数序列,一个是偶数序列,怎么相交?~~),今生无缘~
---
可以粗略地总结出几个规律:
* 新方程与原方程具有同样的形式。
* 新方程的模数,是之前两个模数的$lcm$。
* 可能存在无解的情况
这几条性质是整个算法的基石,我们会在后文详细讨论。这里先一步步摸出 “合并” 的算法。形式化地,考虑这样一组模线性同余方程:
$$\left\{\begin{matrix}
a \equiv r_1 \ (mod \ m_1) \\
a \equiv r_2 \ (mod \ m_2)
\end{matrix}\right.
$$
这个方程组等价于:
$$\large a=k_1m_1+r_1=k_2m_2+r_2$$
移一下项,立刻有
$$\large k_1m_1-k_2m_2=r_2-r_1$$
左边的$m_1,m_2$,右边的$r_2-r_1$是已知量。这就是一个典型的不定方程。解不定方程这个任务,我们是熟悉的:可以通过裴蜀定理判断有没有解,可以用扩展欧几里得算法($exgcd$)给出$k_1,k_2$的整个解系。于是算法有了第一步:
* 如果$gcd(m_1,m_2)|(r_2-r_1)$,则判断方程有解
* 否则,报告无解
接下来考虑如何求出一组 $k_1, k_2$约定几个记号:记$d=gcd(m_1,m_2)$,$p_1=\frac{m_1}{d},p_2=\frac{m_2}{d}$,显然$p_1,p_2$是互质的。那么把$m_1$用$p_1d$来代替,$m_2$用$p_2d$来代替,可以把上面的式子写成:
$$\large k_1p_1-k_2p_2=\frac{r_2-r_1}{d}$$
右边那一串东西是整数,因为当且仅当 $d | (r_2-r_1)$才会有解。**左边满足了$gcd(p_1,p_2)$互质**,因此求出整体解系是很容易的。现在假设我们拿$exgcd$求出了下面这个方程的**特解**($λ_1,λ_2$):
$$\large λ_1p_1+λ_2p_2=1$$
这是一个非常标准的$exgcd$. 求出来了$λ_1,λ_2$之后,可以直接拼出$k_1,k_2$(就是翻整数倍、加上负号等操作):
$$
\large \left\{\begin{matrix}
k_1=\frac{r_2-r_1}{d}λ_1 \\
k_2=-\frac{r_2-r_1}{d}λ_2
\end{matrix}\right.
$$
于是:
$$\large x=r_1+k_1m_1=r_1+\frac{r2-r1}{d}λ_1m_1$$
至此,我们成功地构造出了一个 $x$,满足
$$
\large \left\{\begin{matrix}
x \equiv r_1 \ (mod \ m_1) \\
x \equiv r_2 \ (mod \ m_2)
\end{matrix}\right.
$$
* $r_1,\frac{r2-r1}{d}$是常数,$λ_1$通过$exgcd$可以计算出来,这不就是一个$x=r_1+k'm_1$吗~
* 也就是通过一顿操作,把两个方程等式合并为一个方程等式~
但是整个解系如何给出呢?我们有:
**【定理】**
若方程组
$$
\large \left\{\begin{matrix}
x \equiv r_1 \ (mod \ m_1) \\
x \equiv r_2 \ (mod \ m_2)
\end{matrix}\right.
$$
有特解$x^{'}$,那么通解是:$x^{'}+k\cdot lcm(m_1,m_2)$,亦即
$$\large x \equiv x^{'} \ (mod \ lcm(m_1,m_2)) $$
这里就不进行数学证明了,联想一下最上面的两个式子:
$$ \large
\left\{\begin{matrix}
x \equiv 2(mod \ 4) \\
x \equiv 4(mod \ 6) \\
\end{matrix}\right. \Rightarrow x \equiv 10 (mod \ 12)
$$
参考博客:https://www.cnblogs.com/ailanxier/p/13370753.html
### 合并流程
假设前 $i−1$ 个同余方程合并得到的新方程为:$x \equiv r_1(mod \ M)$, $M$ 是前 $i−1$ 个同余方程模数的**最小公倍数**,现在考虑合并第 $i$ 个方程:$x \equiv r_2(mod \ m_i)$ 。
#### 步骤
1. 前 $i−1$ 个方程,通解为 $x=r_1+q\cdot M,q∈Z$。
<br>
2. 在$q$中找到某些特殊值$t$ ,使得 $$(r_1+t∗M) \equiv r_2 (mod \ m_i) \Rightarrow M∗t+m_i∗y=r_2−r_1$$ ,只有这样,合并完的方程才能满足第 $i$个方程。
<br>
3. 上式中$t,y$是未知的,其它是已知的常数,这就是标准的形如$ax+by=c$的二元一次不定方程,可以用**裴蜀定理**来判断是否有解:
$M$ 就是 $a$ , $t$ 就是 $x$ ,$m_i$就是$b$,$c$就是$r_2-r_1$。
设$d=gcd(M,m_i)$,则 $r_2−r_1$ 要满足 $d|(r_2−r_1)$ ,否则无解。
<br>
4. 在判断有解的情况下,使用**扩展欧几里得**求出方程$ax+by=gcd(a,b)$的一组特解$x_0,y_0$,同时求出 $d$。
<br>
5. 求出的是$ax+by=gcd(a,b)$的解,并不是$ax+by=c$的解,所以,为了拿到$ax+by=c$的一组解,需要把$x_0*\frac{c}{d}$才能得到一个有用的解。 $\large x=x_0∗ \frac{r_2−r_1}{d}$ ,得到方程$M∗t+m_i∗y=r_2−r_1$的一个特解。
<br>
6. 合并后通解: $x=r_1+t∗M+p∗lcm(M,m_i),p∈Z$
依据:这里提到的[【定理】](https://www.luogu.com.cn/blog/blue/kuo-zhan-zhong-guo-sheng-yu-ding-li )
<br>
7. 对通解模 $lcm(M,m_i)$ 即可得到新的 $ans$ 作为前 $k$ 个同余方程的**最小整数解**
<br>
8. 更新 $ans$ 和 $M$ ,让 $M$ 成为前 $i$ 个同余方程模数的最小公倍数。
<br>
#### 实现细节
注意这里得用快速(龟速)乘,否则会爆$long$ $long$ ,而且 $r_2−r_1$ 不能是负数(否则快速乘会 $TLE$ ) , 所以这里都要使用一些取模的技巧。

View File

@@ -0,0 +1,195 @@
### 扩展欧几里得算法
我们知道,计算**两个正整数的最大公约数**有两个比较常用的方法:**更相减损术**和**辗转相除法**,其中辗转相除法也叫欧几里得算法:$gcd(a,b)=gcd(b,a \%b)$
> 更相减损术和辗转相除法的主要区别在于前者所使用的运算是“**减**”,后者是“**除**”。从算法思想上看,两者并没有本质上的区别,但是在计算过程中,如果遇到一个数很大,另一个数比较小的情况,可能要进行很多次减法才能达到一次除法的效果,从而使得算法的时间复杂度退化为$O(N)$,其中$N$是原先的两个数中较大的一个。相比之下,辗转相除法的时间复杂度稳定于$O(logN)$。
#### 一、欧几里得和扩展欧几里得
* 欧几里得算法,就是人们常说的辗转相除法,比较好理解,主要作用是求两个数最大公约数,最小公倍数也可方便的求出
* 扩展欧几里得就非常神奇了,主要作用是解不定方程, 即 $a * x + b * y = c$ ,我们都知道可以有解,但不是唯一解
* 用 $exgcd$ 可以很快求出 $a * x + b * y = gcd(a,b)$ 的一个特解。如果 $gcd(a,b) | c$ 即,有整数解,否则无整数解。
利用**二元一次不定方程的通解形式**一文中的证明,知道通解:
$$\large x=x_0*k+\frac{b}{(a,b)}\cdot t $$
$$\large y=y_0*k-\frac{a}{(a,b)}\cdot t$$
其中$t$为任意整数,$\large k=\frac{c}{gcd(a,b)}$
#### 二、扩展欧几里得实现过程
裴蜀等式求解过程:
应用欧几里得算法是一个递归的过程,将规模较大的问题转化为等价的小问题去解决,核心代码就是$gcd(a,b)=gcd(b,a\%b)$,证明也略去了(笔者数学一般,见谅)。
它的边界情况出现在 $gcd(k,0)$ 的时候,这时$k$就是 $gcd(a,b)$ 啦。而扩展欧几里得算法则多了几个额外的步骤,在递归返回的时候做了一些小操作,使得我们可以得到 $ax+by=gcd(a,b)$ 的一组解。思路如下:
1. 第一层: $ax+by=gcd(a,b)$,递归到下一层,$gcd(a,b)=gcd(b,a\%b)$, 同时向下一层传 $x$ 和 $y$ ,设为$x′$和$y′$。
<br>
2. 第二层中:$bx′+a\%b∗y′=gcd(b,a\%b)$ ,我们可以把 $a\%b$ 表示为 $a−⌊\frac{a}{b}⌋\cdot b$ ,代入整理可得
$$bx′+(a-⌊\frac{a}{b}⌋\cdot b)*y′=gcd(b,a\%b)$$
$$\Rightarrow $$
$$ay′+b(x′−⌊\frac{a}{b}⌋ y′)=gcd(b,a\%b)=gcd(a,b)$$
比较可得,第一层中的 $x$ 和 $y$ 和第二层中 $x′$和 $y′$ 的关系为
$$\large x=y′,y=x′−⌊\frac{a}{b}⌋y′ $$
<br>
3. 第二层的 $x′$和 $y′$ 又可以由第三层的 $x′′$和 $y′′$ 代入同样的求得,在递归终点的时,我们只需让 $x=1$ $y=0$即可。
注意因为上一层需要知道下一层的 $x$ 和 $y$ ,所以我们可以用传引用的方法,在递归返回时 $x$ 和 $y$ 就是下一层的 $x′$ 和 $y′$ 了。
#### 扩展欧几里得解不定方程的步骤
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
/* 原始方程 4x+6y=10 求x,y的一组解
分析及求解步骤:
此题中a=4,b=6,c=10
1. 求d=gcd(a,b)=gcd(4,6)=2
2. 判断 2|c即 10%2=0,方程有解,如果不能整除,此处可以直接返回方程无整数解
3. 化简方程为 a/d*x+b/d*x=c/d 得到 4/2*x+6/2*y=10/2
=> 2x+3y=5 此方程与原始方程解是一样的,我们最终对这个方程下手。
4. 如可解此方程呢?我们有一个好用的定理:扩展欧几里得可以解类似的方程
ax+by=gcd(a,b)!
但问题是按扩欧的描述,我们现在能计算的是 2x+3y=1的方程解!!!
不是原方程2x+3y=5的解!!
该怎么办呢?
设 x0,y0为2x+3y=1的解,那么原方程的一组解就是:
x1=5*x0,y1=5*y0!!!!
为什么呢?原因很简单: 2x+3y=1 左右都乘以5
2*(x*5)+3*(y*5)=5
对比一下原方程 x1=x0*5,y1=y0*5啊~
*/
LL x, y;
exgcd(2, 3, x, y);
printf("2x+3y=1 -> x=%lld y=%lld\n", x, y);
printf("2x+3y=5 -> x=%lld y=%lld", 5 * x, 5 * y);
return 0;
}
```
#### 练习题I
[P1082 [NOIP2012 提高组] 同余方程](https://www.luogu.com.cn/problem/P1082)
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// P1082 [NOIP2012 提高组] 同余方程
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
LL a, b;
scanf("%lld %lld", &a, &b);
LL x, y;
exgcd(a, b, x, y);
printf("%d\n", (x % b + b) % b);
return 0;
}
```
#### 练习题II
P5656 [【模板】二元一次不定方程 (exgcd)](https://www.luogu.com.cn/problem/P5656)
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL 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 x, y;
LL d = __gcd(a, b); //使用库函数__gcd计算最大公约数d
if (c % d) //裴蜀定理,当gcd(a,b)不能整除c时,方程无解,输出-1
puts("-1");
else {
//这步很值得总结:一个方程拿到手,第一步是化简,就是把a,b,c中都除以gcd(a,b),实现化简
//举个栗子:2x+4y=8 -> x+2y=4 很直白吧,套路吧~ 此方程与原方程的角系一样吧~
//当然,也可能化简不了,比如: 2x+3y=5,因为gcd(2,3)=1,化不化简都一样
//但这里有一个事实,因为 a'=a/gcd(a,b),b'=b/gcd(a,b)后,a',b'肯定是互质。
//令d'=gcd(a',b') 现在d'=1
a /= d, b /= d, c /= d;
//扩展欧几里得,一通用形式返回d,在有的题解中,不采用exgcd返回d,而是用__gcd计算d.
//把扩展欧几里得算法定位为取特解x,y专用,也是比较直观简便的。
//性能上嘛,由于辗转相除法不断的变更除数,速度上很快,两次也没有问题。
//扩欧的形式:ax+by=gcd(a,b),求方程的一组解 x,y。现在的情况下,gcd(a,b)=1了,可以
exgcd(a, b, x, y);
x *= c, y *= c; //结果(x,y)需要乘以c才能还原为ax+by=c的方程解。
/*
通解公式: x=x0+b/d*t
y=y0-a/d*t
此时d=1,则通解公式:
x=x0+b*t
y=y0-a*t
想要xmin => x0固定值,b固定值,只能变化的是t,每变化一次就增加、减少一个b
*/
LL xmin = (x % b + b) % b; //利用最小正整数解的公式
if (xmin == 0) xmin = b; // xmin如果是0,需要特判,加一个b搞定~
LL ymin = (y % a + a) % a;
if (ymin == 0) ymin = a; // ymin如果是0,需要特判,加一个a搞定~
// x最大值怎么求呢?简单:ax+by=c => ax=c-by => x = (c-by)/a
// x要想最大,则y必须取最小值
LL xmax = (c - b * ymin) / a;
// y要想最大,则x必须取最小值
LL ymax = (c - a * xmin) / b;
if (xmax <= 0 || ymax <= 0) //若最大值还不是正整数,则无正整数解
printf("%lld %lld\n", xmin, ymin); //输出最小值
else {
//解的个数是最大 x 最小 x 的差除以模数 b 再加上1。
LL sum = (xmax - xmin) / b + 1; //不足b时,也有1个解,+b就增加一个解
printf("%lld %lld %lld %lld %lld\n", sum, xmin, ymin, xmax, ymax);
}
}
}
return 0;
}
```

View File

@@ -0,0 +1,41 @@
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
//扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
/* 原始方程 4x+6y=10 求x,y的一组解
分析及求解步骤:
此题中a=4,b=6,c=10
1. 求d=gcd(a,b)=gcd(4,6)=2
2. 判断 2|c即 10%2=0,方程有解,如果不能整除,此处可以直接返回方程无整数解
3. 化简方程为 a/d*x+b/d*x=c/d 得到 4/2*x+6/2*y=10/2
=> 2x+3y=5 此方程与原始方程解是一样的,我们最终对这个方程下手。
4. 如可解此方程呢?我们有一个好用的定理:扩展欧几里得可以解类似的方程
ax+by=gcd(a,b)!
但问题是按扩欧的描述,我们现在能计算的是 2x+3y=1的方程解!!!
不是原方程2x+3y=5的解!!
该怎么办呢?
设 x0,y0为2x+3y=1的解,那么原方程的一组解就是:
x1=5*x0,y1=5*y0!!!!
为什么呢?原因很简单: 2x+3y=1 左右都乘以5
2*(x*5)+3*(y*5)=5
对比一下原方程 x1=x0*5,y1=y0*5啊~
*/
LL x, y;
exgcd(2, 3, x, y);
printf("2x+3y=1 -> x=%lld y=%lld\n", x, y);
printf("2x+3y=5 -> x=%lld y=%lld", 5 * x, 5 * y);
return 0;
}

View File

@@ -0,0 +1,184 @@
### 一、欧几里德定理
欧几里德定理: $gcd(a, b) = gcd(b , a\%b)$
欧几里德算法又称辗转相除法,**用于计算两个整数$a,b$的最大公约数**。
他避免了我们去枚举$a,b$的因子,让我们可以在几乎是 $log$ 的时间复杂度里求解出来 $a$ 和 $b$ 的**最大公约数**。
基于以上定理,我们就可以很轻松求出,$a$和$b$的最大公因数,对于一般的欧几里得算法就不再都说什么了,模板:
```c++
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
```
**证明**:
设 $a>b$
令 $c=a\%b$
可得:$a=k*b+c$ ($k$最小是$1$,$c$肯定小于$b$)
如果$c$为$b$的一个约数,即$b\%c=0$
则$c$也一定$a$的约数,因为$a\%c=(k*b+c)\%c=0$
这时,$c$即为$a$和$b$的最大公约数。
如果$c$不为$b$的约数,设$b\%c=d$
同理,如果$d$是$c$的一个因数,那$d$也一定是$a、b$的因数
重复上诉操作,直至求出一个满足条件的数,即为所求。
**举栗子**:
以求 $46$ 与 $28$ 的最大公约数为例:
第一次迭代, $a = 44, b = 28$, 较小数为 $28$, 余数为 $16$。
第二次迭代, $a = 28, b = 16$, 较小数为 $16$, 余数为 $12$。
第三次迭代, $a = 16, b = 12$, 较小数为 $12$, 余数为 $4$。
第四次迭代, $a = 12, b = 4$, 较小数为 $4$, 余数为 $0$。
第五次迭代, $a = 4, b = 0$, 因为$0$能被任何整数整除,易得$4$和$0$的最大公约数为$4$,算法结束。
观察每次迭代的$a$ 和 $b$, 不难发现, $<44,28>$, $<28,16>$ , $<16,12>$ , $<12,4>$的最大公约数都是$4$。即上述定理所说的,两个整数的最大公约数等于其中较小的数和两数相除余数的最大公约数。
### 二、[裴蜀定理](https://baike.baidu.com/item/%E8%A3%B4%E8%9C%80%E5%AE%9A%E7%90%86/5186593?fr=aladdin)
裴蜀定理(或贝祖定理)得名于法国数学家艾蒂安·裴蜀,说明了对任何整数$a$、$b$和它们的最大公约数$d$,关于未知数$x$和$y$的线性不定方程(称为裴蜀等式):若$a$,$b$是整数,且$gcd(a,b)=d$,那么**对于任意的整数$x$,$y$,$ax+by$都一定是$d$的倍数,特别地,一定存在整数$x$,$y$,使$ax+by=d$成立**。
**推论:$a$,$b$互质的充分必要条件是存在整数$x$,$y$使$ax+by=1$。**
**举栗子**:
$2x+y=3$
那么$a=2$,$b=1$,$m=3$,$gcd(2,1)=1$,$3$是$1$的倍数,所以这个方程一定有整数解。
比如$x=1$,$y=1$就是一个整数解,还可以有$x=-2$,$y=7$也是一组整数解。
**注意:如果一个二元一次方程有正整数解,那么不只是一组解。**
**再举一个栗子**:
$4x+2y=5$
有没有整数解呢?没有,因为$a=4,b=2,m=5,gcd(4,2)=2,5$是除不开$2$的,所以没有整数解!
### 三、[求贝祖数(特解)](https://www.bilibili.com/video/av77974575/)
什么是贝祖数?
比如:$2x+y=3$,那么符合这个等式的$x=1$,$y=1$就是一组贝祖数。
在计算机算法中,可以使用**扩展欧几里得算法求贝祖数**。
举个栗子:
求 $104x+40y=8$ ,有没有合适的$x,y$,也就是求贝祖数。
通过贝祖定理看一下,它是不是有解: $gcd(104,40)=8$,$8$是$8$的倍数,所以此方程一定有解,那么解是什么呢?
![QQ截图20210301141004.png](https://cdn.acwing.com/media/article/image/2021/03/01/64630_d415a5dc7a-QQ截图20210301141004.png)
**扩展欧几里得算法的推导过程:**
我们先来求方程 $ax+by=gcd(a,b)$的解
($1$)、当 $b=0$时
$ax+by=gcd(a,0)$,也就是:$ax=gcd(a,0)=a$,所以$x=1$。
那么$y$呢?因为 <font color='red'><b>普遍意义上的求贝祖数,一般是指不小于零的一组解</b></font> ,那么不小于$0$的第一个可能$y$值就是:$y=0$,所以,可以将$x=1,y=0$做为一组**特解**返回,也就是返回了一组**不小于零的贝祖数**。
($2$)、当 $b≠0$时
$\because gcd(a,b)=gcd(b,a\%b)$
并且$①\ \ \ \ ax+by=gcd(a,b)$
$\therefore bx′+(a\%b)y′=gcd(a,b)$
$\because a−⌊a/b⌋∗b=a\%b$
$\therefore bx′+(a−⌊a/b⌋∗b)y′=gcd(a,b)$
变形一下:
$② \ \ \ \ ay′+b(x′−⌊a/b⌋∗y′)=gcd(a,b)$
对比$① ②$系数:
$\therefore$ $x=y′,y=x′−⌊a/b⌋∗y′$
因此可以采取递归算法 先求出下一层的$x′$和$y′$ 再利用上述公式回代即可
```c++
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
```
### 四、通解公式
有了上面求出的特解,我们就可以得到通解公式:
$a*x+b*y=c$ ①
$a*x_0+b*y_0=c$ ②
联立得到:
$a*(x-x_0)=b*(y_0-y)$
等式两边同时除以$gcd(a,b)$得到:
$\displaystyle \frac{a}{gcd(a,b)}*(x-x_0)=\frac{b}{gcd(a,b)}*(y_0-y)$
我们知道,若有两个数$a$和$b$,他们同时除以**最大公约数**,之后的商是**互质**的。所以在上面等式中,如果成立,那么说明,$\displaystyle \frac{b}{gcd(a,b)}$一定是 $(x - x_0)$的倍数,而 $\displaystyle \frac{a}{gcd(a,b)}$ 一定是$(y_0-y)$的倍数。
所以 $\displaystyle x-x_0=\frac{b}{gcd(a,b)}*k$ , $\displaystyle y_0-y=\frac{a}{gcd(a,b)}*k$
所以 $$x=x_0+\frac{b}{gcd(a,b)}*k$$
$$y=y_0-\frac{a}{gcd(a,b)}*k$$
### 五、扩展欧几里德算法的应用
https://zhuanlan.zhihu.com/p/42707457
#### 1、求解不定方程
[AcWing 877. 扩展欧几里得算法](https://www.acwing.com/problem/content/879/)
[AcWing 203. 同余方程](https://www.acwing.com/problem/content/205/)
#### 2、求解线性同余方程
给我们$a,b,c$,让我们求一个整数$x$,使得$ax \equiv c (mod \ b)$。
就等价于 $ax-c$是$b$的整数倍,假设倍数是$y$,则有:$ax-c=by$
变形:$ax-by=c$,由于倍数是可正可负,所以可以认为把负号吃到变量$y$中,方程就是:
$$ax \equiv c (mod \ b) \Leftrightarrow ax+by=c$$
这玩意咋理解呢?我们可以想像一下生活中的例子,比如$1$号是星期一,那么$8$号也是星期一。因为一周有$7$天,$7$天一循环嘛。
此时 我们认为 $$1 \equiv 8 (mod \ 7)$$
说的再直白些,就是从一个大数当中,扣去循环节的整数倍,得到的余数与小数是相等的。
#### 3、求解模的逆元
练习题
POJ -1061青蛙的约会
https://blog.csdn.net/GD_ONE/article/details/96479556
luogu P5656
同余方程、青蛙的约会、最幸运的数字
一.一元线性同余方程
poj 1061 2115 2142
二.一元线性同余方程组
先看一篇博客
对于模线性方程组,可以进行两两合并,求出方程的最终解。
poj 2891 (最小正整数解)
hdu 1573
求一定范围内的解的个数
hdu3579
输出最小正整数解

View File

@@ -0,0 +1,33 @@
### 最大公约数的性质
#### 性质1
$gcd(a,a)=a$ //一个数与自己的最大公约数是本身
#### 性质2
$gcd(ka,kb)=k*gcd(a,b)$ //分配律
#### 性质3
$gcd(a, b, c) = gcd(gcd(a, b), c)$ //结合律
#### 性质4
若$gcd(k,b)=1$,则$gcd(ka,b)=gcd(a,b)$
#### 性质5
$gcd(a,b) = gcd(b,a-b)$ //辗转相除法
$gcd(a_1,a_2,a_3,...a_n)=gcd(a_1,a_2-a_1,a_3-a_2,...,a_n-a_{n-1})$
#### 性质6
$gcd(a,b) = gcd(a, a+b) = gcd(a,ka+b)$
#### 性质7
若$gcd(a, b) = d$,则$gcd(a/d, b/d) = 1$,即$a/d$与$b/d$互素。这个定理很重要。
### 常见题套路
$gcd$满足区间可加性,区间的$gcd$问题常常使用线段树维护
习题:https://codeforces.com/contest/1459/problem/C
https://blog.csdn.net/qq_53629286/article/details/122692425
https://blog.csdn.net/weixin_44316314/article/details/105892187 这里面有一些我未学习过的性质,等功力到了再来看

View File

@@ -0,0 +1,117 @@
## 欧拉定理(从理论到应用)
费马小定理和欧拉定理、威尔逊定理、中国剩余定理并称为初等数论四大定理。
### 1、引入基本概念
#### 1.1 互质
公约数只有$1$的两个 **整数**,称为互质。$a$与$b$互质,则写作 $(a,b)=1$。
#### 1.2 质因数
质因数指能**整除给定整数的质数**,例如$6$的质因数为$2$和$3$。
#### 1.3 余数的基本性质
$(a+b)\%c = ((a\%c)+(b\%c)) \% c$
$(a-b)\%c = ((a\%c) - (b\%c)) \% c$
$(a*b)\%c = ((a\%c) * (b\%c)) \% c$
<font color='red'><b>注意:除法没有这个性质,需要用到逆元!</b></font>
#### 1.4 同余
给定一个正整数$m$,如果两个整数$a$和$b$满足$a-b$能够被$m$整除,那么就称整数$a$与$b$对模$m$同余,记作$a≡b(mod \ m)$。
这玩意咋理解呢?我们可以想像一下生活中的例子,比如$1$号是星期一,那么$8$号也是星期一。因为一周有$7$天,$7$天一循环嘛。
此时 我们认为 $$1 \equiv 8 (mod \ 7)$$
说的再直白些,就是从一个大数当中,扣去循环节的整数倍,得到的余数与小数是相等的。
### 2、欧拉函数
#### 2.1 定义
对正整数$n$,欧拉函数是小于$n$的正整数中与$n$互质的数的数目。
#### 2.2 欧拉函数通项公式
$φ(n)=n*(1-1/p_1)*(1-1/p_2)*(1-1/p_3)*(1-1/p_4)*……*(1-1/p_n)$
其中,$p$为$n$的质因数,$n$是不为$0$的正整数,$φ(1)=1$,质数的$φ$为自己减$1$ 例如$φ(2)=1$
1. 直接求
2. 筛法求
还可以在素数筛的同时求欧拉函数,效率更高。
这一块的内容,直接看我单独总结的欧拉函数章节,这里不再赘述。
### 3、欧拉定理
数学世界中最美妙的定理,也称费马-欧拉定理或欧拉函数定理。莱昂哈德·欧拉,瑞士数学家,极其牛$B$的大神,没有之一。
#### 3.1定理内容
若 $a, n$为正整数,且<font color='red'><b>$a,n$互质</b></font> ,则 $$\large a^{φ(n)}≡1(mod \ n)$$
其中$\phi(n)$指欧拉函数值。
#### 3.2 证明
[参考文档](https://blog.csdn.net/ymzqwq/article/details/96269772)
设$X_1,X_2,X_3,...,X_{\phi(n)}$ 是$[1 \sim n)$里与$n$互质的数,容易发现他们
* **<font color='red'><b>模$n$两两不同</b></font>**
* **<font color='red'><b>余数都与$n$互质</b></font>**。
其实这个不证明也行,都是废话,$X_i \%n$还是$X_i$,$X_i$当然两两不同,模了之后的余数就是$X_i$自己本身,可不是还与$n$互质。
然后我们发现$aX_1,aX_2,...,aX_{\phi(n)}$ 也有上面两个性质:<font color='red'><b>(用到条件:$a,n$互质)</b></font>
* <font color='red'><b>模$n$ 两两不同</b></font>
**反证法**:
若$aX_i \equiv aX_j (mod \ n) \Leftrightarrow aX_i-aX_j \equiv 0 (mod \ n) \Leftrightarrow a(X_i-X_j) \equiv 0 (mod \ n)$
,由于$gcd(a,n)=1$,所以$X_i-X_j$必然是$n$的倍数!而我们知道$X_i$,$X_j$都是比$n$小的,差不可能是$n$的倍数,出现矛盾,假设错误,不存在任意两个$aX_i \equiv aX_j (mod \ n)$,即$aX_1,aX_2,...,aX_{\phi(n)}$模$n$两两不同。
* <font color='red'><b>余数都与 $n$ 互质</b></font>
$a$ 与 $n$ 互质,$X_i$ 与 $n$ 互质,所以 $aX_i$也与$n$互质
有了这两个性质,我们就可以发现$aX_1,aX_2,aX_3,...,aX_{\phi(n)}$模$n$后一定是$\phi(n)$个不同的与$n$互质的数,那不就肯定是$X_1,X_2,X_3,...,X_{\phi(n)}$这个集合嘛。
所以得到
$$X_1 \cdot X_2 \cdot X_3 \cdot ... \cdot X_{\phi(n)} \equiv aX_1 \cdot aX_2 \cdot aX_3 \cdot ... \cdot aX_{\phi(n)} (mod \ n)$$
$$\Leftrightarrow$$
$$a^{\phi(n)} \equiv 1 (mod \ n)$$
$\large QED$ 证毕~
#### 3.3 费马小定理
若存在整数$a,p$,$a$为整数,$p$为质数,那么$a^{(p-1)}≡ 1(mod \ p)$。
<font color='red'><b>费马小定理是欧拉定理的一种特殊情况</b></font> (当$n$为质数时$φ(n)$为$n-1$)
#### 3.4 费马小定理应用
见我整理的费马小定理章节。
#### 3.5 欧拉定理应用
首先看一个基本的例子。令$a = 3,n = 5$,这两个数是互素的。比$5$小的正整数中与$5$互素的数有$1、2、3$和$4$,所以$φ(5)=4$。
计算:$a^{φ(n)} = 3^4 =81$,而$81= 80 + 1 \equiv 1 (mod \ 5)$。与定理结果相符。
这个定理可以用来简化幂的模运算。比如计算$7^{222}$的个位数,实际是求$7^{222}$被$10$除的余数。$7$和$10$**互素**,且比$10$小的正整数中与$10$互素的数有$1,3,7,9$,$φ(10)=4$。由欧拉定理知$7^4 \equiv 1(mod \ 10)$。所以$7^{222}=7^{4*55}*7^2 \equiv 1^{55}*7^2 \equiv 49 \equiv 9 (mod \ 10)$。
#### 3.6 推论:最小正整数解的性质
满足欧拉定理的最小正整数 $x$一定是$φ(c)$的约数,即 $x|φ(c)$,如果想找到最小正整数$x$,可以枚举$\phi(c)$的所有约数即可。
#### 3.7 推论证明
反证法:
假设该最小正整数 $x$ 不是 $φ(c)$ 的约数
则$\displaystyle \large \phi(c)=qx+r \ \ \ \ \ (0<r<x) $
又 $\displaystyle \large
\left\{\begin{matrix}
10^x \equiv 1 (mod \ c) \Rightarrow 10^{qx} \equiv 1 (mod \ \ c ) \ \ ①\\
10^{\phi(c)} \equiv 1 (mod \ \ c ) \Rightarrow 10^{qx+r} \equiv 1 (mod \ \ c ) \ \ ②
\end{matrix}\right.
$
由$2$式除以$1$式得$$\large 10^r \equiv 1 (mod \ \ c ) \ \ \ \ \ (0<r<x)$$
故存在比 $x$ 更小的正整数满足该式子,矛盾了,因此得证。
### 4、应用及拓展
#### 4.1求逆元
#### 4.2欧拉降幂
https://blog.csdn.net/qq_51922387/article/details/122401181

View File

@@ -0,0 +1,9 @@
#### 裴蜀等式
裴蜀定理$ax+by=gcd(a,b)$
> 同样是看名字一脸懵的定理,还有这个字念"pei",不知打错了多少次“翡”)得名于法国数学家艾蒂安·裴蜀
说明了对任何整数$a、b$和它们的最大公约数$gcd(a,b)$,关于未知数 $x$ 和 $y$ 的线性二元一次不定方程(称为**裴蜀等式**):
一定存在整数$x,y$,使$ax+by=gcd(a,b)$成立。
它的一个重要推论是:$a,b$互质的充要条件是存在整数 $x,y$ 使$ax+by=1$ 。证明我就略去了,来讲一下扩展欧几里得算法怎么得到裴蜀等式的一个解(有多个解,求出一个解可以写出通解)。

View File

@@ -0,0 +1,34 @@
### 问题:$13$能否整除$9^{36}-1$、$9^{39}-1$?
#### 费马小定理
假如$p$是质数,且$gcd(a,p)=1$,那么 <font color='red'><b>$$\huge \displaystyle a^{p-1}≡1\ (mod \ p)$$
</b></font>
本题中,$p=13$,是质数。$a=9$,$gcd(a,p)=1$,满足费马小定理,所以利用其得到结论:
$\large \displaystyle \because 9^{p-1}=9^{13-1}=9^{12}$
$\large \displaystyle \therefore 9^{12}\equiv 1 (mod \ 13)$
$\large \displaystyle \therefore (9^{12})^3\equiv 1 (mod \ 13)$
即
$\large \displaystyle \therefore 9^{36}\equiv 1 (mod \ 13)$
也就是$\large \displaystyle 13 | 9^{36} -1 $
---
再来看一下$\large \displaystyle 9^{39}-1$:
$\large \displaystyle 9^{39}\equiv 9^{36} \times 9^3 (mod \ 13) $
$\large \displaystyle \because 9^{36} \equiv 1 (mod \ 13) $
$\large \displaystyle 9^3=729=(13 \times 56 +1)=1 (mod \ 13)$
也就是$\large \displaystyle 13 | 9^{39} -1 $
<font color='red'><b>总结:</b></font>
望文知义,看着像就往定理上套,这个思路很明显啊,王道!

View File

@@ -0,0 +1,189 @@
### 一、定理内容
[讲解视频](https://haokan.baidu.com/v?pd=wisenatural&vid=3032136584887251853)
**费马小定理**
如果$p$是一个质数,而整数$a$不是$p$的倍数
$$\large a^{p}\equiv a (mod \ p) $$
$$\large \Leftrightarrow $$
$$\large a^{p-1} \equiv 1 (mod \ p)$$
#### 1.1 这玩意有啥用?
题目:一个多位数有$2020$位,它左边三位为$123$。当这个数最大时,它被$29$除所得的余数是_______。
在小学奥数中,经常会用到费马小定理解决余数问题。
* 先举一个简单的例子:
比如计算$18^{111}$ 除以$23$的余数是多少。
在这里,$a=18$, $p=23$是素数,根据所以使用费马小定理,我们知道$18^{(23-1)}$除以$23$余$1$,即:
$18^{111} \equiv 18^{22 \times 5 +1} \equiv 18^{22 \times 5 } \times 18 \equiv 18 (mod 23)$
所以答案就是:$18$。
* 回到这个问题:
这个$2020$位数最大时即为$123$后面是$2017$个$9$。
我们设这个数是$a$, 则$a= 124 \times 10^{2017}-1$。
这个问题就转变为$124 \times 10^{2017}-1$除以$29$的余数是多少?
我们用同余运算和费马小定理有:
$124 \times 10^{2017} -1$
$\equiv 8 \times 10^{72\times 28+1}-1$ 这里因为$124$去除$29$,商是$4$,余数是$8$。商不会影响同余运算,舍去,保留余数$8$
$\equiv 8 \times 10^1 -1$
$\equiv 80-1$
$\equiv 21 (mod \ 29)$
答案就是:$21$
怎么样,超简单吧?
那么,如果$p$不是素数时,这种问题又该怎么处理呢?
这时候欧拉定理和欧拉函数就要大显身手啦。
#### 1.2 可以用来降幂
$a^n \ \% p = a^{n \ \% (p-1)} \ \% p$
**求$a$的$n$次方,可以先$n \ \% (p-1)$**
解释一下:
费马小定理降幂例子:
$p=7$
求$2^{32}\ \%p$
这个幂指数很大,我们不会傻傻的真的去计算出$2^{32}$,而是利用费马小定理对指数进行缩减:
因为$p=7$是质数,所以使用费马小降幂,$p-1=6$
$2^6\%p=1$
在$32$里面,把所有$6$的倍数去掉,就是$2^{32-6*5}=2^2=4$
也就是说,我们可以把原来的$32$降为$4$,最终的答案是一样的结果。
写成通用的格式就是:
求$(a^N)mod(p)$
$a^{p-1} \equiv 1(mod \ p)$
$k=N \% (p-1)$
$(a^N)mod(p)=(a^k)mod(p)$
### 二、例题I
求$2^{100}\%13$的余数。
$2$的$100$次方,明显幂次挺大,而且$13$是质数,可以用费马小定理来降幂。
$$\large 2^{100} \equiv 2^{12*8+4} \equiv 16 \equiv 3 (mod \%13)$$
```c++
#include <bits/stdc++.h>
using namespace std;
//计算2^100 %13的值
int fun1(int a, int p, int mod) {
//前提p是质数,a不是p的倍数,有费马小定理
int ans = pow(a, p % (mod - 1)); //利用费马小定理,降幂 p'= p % (mod - 1),然后再计算 pow(a,p'),这个就小多了
ans %= mod; //再最后模一下mod就可以了
return ans;
}
// fun2和fun3都会受到时间或者空间的限制,而fun1直接使用了费马小定理可以迅速的求取答案
int fun2(int a, int p, int mod) {
int ans = pow(a, p); //不管p多大,我就是个算~暴力!但问题是你不怕爆掉int上界?不会溢出?
ans %= mod; //最后取模
return ans;
}
int fun3(int a, int p, int mod) {
int ans = 1;
while (p--) { //既然怕爆int上界,那么就一步一取模,一步一计算。笨的要死,你不怕慢死啊:人家费马小定理只需要一步,你需要p步!而且,p还不小,慢的要死!
ans *= a;
ans %= mod;
}
ans %= mod;
return ans;
}
int main() {
for (int i = 1; i <= 100; i++) {
cout << fun1(2, i, 13) << endl;
cout << fun2(2, i, 13) << endl;
cout << fun3(2, i, 13) << endl;
}
return 0;
}
```
### 二、例题II
<center><img src='https://img2018.cnblogs.com/blog/1454456/201903/1454456-20190321213031984-364307702.png'></center>
**解题思路**
因为模数是$101$,比较小,而幂$n$是$2019^{2019}$,很大!所以使用费马小降幂$n\%(p-1)$,这里$p$就是$101-1 = 100$;
```c++
int n = 1, ans = 0;
for (int i = 1; i <= 2019; i++) n = n * 2019 % 100;
```
也就是说,在不断的循环计算$n$的过程中,我们利用费马小定理,找到了一个$n'$,使得$n'$与原数$n$对于结果的贡献是一样的,但$n'$明显小于$n$,方便计算出来结果。
**实现代码**
```c++
#include <bits/stdc++.h>
using namespace std;
int main() {
int n = 1, ans = 0;
for (int i = 1; i <= 2019; i++) n = n * 2019 % 100; //计算出n'
// 1~11的n次方
for (int i = 1; i <= 11; i++) {
int x = 1;
for (int j = 1; j <= n; j++) x = x * i % 101; //降幂后可以正常按要求计算
//收集答案
ans += x;
}
//最终也要模一下101
printf("%d\n", ans % 101);
return 0;
}
```
### 三、例题III
<center><img src='https://img2018.cnblogs.com/blog/1454456/201903/1454456-20190321213416622-1991394410.png'></center>
**解题思路**
项数比$mod$大很多,$2019$比$10086$小,所以不用费马小定理,用**循环周期+快速幂**做
<center><img src='https://img2018.cnblogs.com/blog/1454456/201903/1454456-20190321213519548-140956041.png'></center>
**实现代码**
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 10086;
LL pow_mod(LL x, LL p) {
LL res = 1;
while (p) {
if (p & 1) res = res * x % mod;
p >>= 1;
x = x * x % mod;
}
return res;
}
LL ans;
LL n = 1e12;
int main() {
for (int i = 1; i <= mod; i++) ans = (ans + pow_mod(i, 2019)) % mod; //求到10086 一个循环周期的长度
ans = ans * (n / mod) % mod; //乘上倍数
n %= mod; //再加上余数
for (int i = 1; i <= n; i++) ans = (ans + pow_mod(i, 2019)) % mod;
printf("%lld\n", ans);
return 0;
}
```

View File

@@ -0,0 +1,42 @@
// https://vjudge.csgrandeur.cn/problem/HDU-1576
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/*
测试用例
2
1000 53
87 123456789
答案
7922
6060
总结:
1、如果使用费马小定理+快速幂求逆元,一定要注意使用LL,防止爆int。
*/
const int MOD = 9973;
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p; //如果res是int,可能会越界
k >>= 1;
a = a * a % p; //如果a是int, 可能会越界
}
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
LL a, b;
cin >> a >> b;
LL x;
x = qmi(b, MOD - 2, MOD); // b理解为分母,同余MOD的逆元,变除法为乘法
LL res = a * x % MOD; // a* b在模MOD下的逆元 x
printf("%lld\n", res);
}
return 0;
}

View File

@@ -0,0 +1,38 @@
// https://vjudge.csgrandeur.cn/problem/HDU-1576
#include <bits/stdc++.h>
typedef long long LL;
/*
测试用例
2
1000 53
87 123456789
答案
7922
6060
*/
using namespace std;
const int MOD = 9973;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int t;
cin >> t;
while (t--) {
LL x, y;
LL a, b;
cin >> a >> b;
exgcd(b, MOD, x, y);
x = (x % MOD + MOD) % MOD; //对比使用费马小定理,这里对于扩展欧几里得算出的逆元,还需要进一步模一下 MOD才行,否则可能是负的
cout << (x * a) % MOD << endl;
}
return 0;
}

View File

@@ -0,0 +1,23 @@
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3000010;
//递推打表求逆元
LL inv[N];
void getInv(LL n, LL p) {
inv[1] = 1;
for (int i = 2; i <= n; i++)
inv[i] = inv[p % i] * (p - p / i) % p;
}
int main() {
LL n, p;
cin >> n >> p;
getInv(n, p);
for (int i = 1; i <= n; i++) printf("%d\n", inv[i]); //可以AC
// cout << inv[i] << "\n"; //可以AC
// cout << inv[i] << endl; //这里使用cout就会TLE两个点!
return 0;
}

View File

@@ -0,0 +1,21 @@
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
int main() {
int a, p;
cin >> a >> p;
LL x = qmi(a, p - 2, p); // x为a在mod p意义下的逆元
return 0;
}

View File

@@ -0,0 +1,112 @@
## 乘法逆元小结
https://www.freesion.com/article/43361201366/
乘法逆元,一般用于求
$$\large \frac{a}{b} \ \ \ (mod \ \ p)$$
的值,($p$通常为质数),是解决模意义下分数数值的必要手段。
### 一、逆元定义
<center><img src='https://www.freesion.com/images/942/0ff480d6440f4723342f4c094b9abde6.png'></center>
很明显,上述式子只有一条不成立,即 $(a/b) \% p$,但是在有些情况下,又需要进行有模运算的除法。因为式子不成立,如果 $a$ 过大或者 $b$ 过大,极易导致计算过程丢失精度,造成结果误差。因此,为了杜绝这一现象,**逆元**出现了。
> 若$a\times x \equiv 1 (mod \ b)$,且$a$与$b$互质,那么我们就能定义:
> $x$为$a$的逆元,记为$a^{-1}$,所以我们也可以称$x$为$a$在$mod \ b$意义下的**倒数**。
> 所以对于 $\frac{a}{b}\ (mod \ p)$ ,我们就可以求出$b$ 在 $mod \ p$ 下的逆元,然后乘上 $a$ ,再 $mod \ p$,就是这个分数的值了。
### 二、求解逆元的方式
#### 1、费马小定理+快速幂求逆元(p一定要质数)
**费马小定理**
> 若$p$为素数,$a$为正整数,且$a、p$互质。则有$a^{p−1}≡1(mod \ p)$。
这个我们就可以发现它这个式子右边刚好为 $1$ 。
所以我们就可以放入原式,就可以得到:
$$\large a \times x \equiv 1 (mod \ \ p )$$
$$\large a \times x \equiv a^{p-1} (mod \ \ p ) $$
$$\large x \equiv a^{p-2} (mod \ \ p )$$
所以我们可以用快速幂来算出 $a^{p−2}(mod \ p)$的值,这个数就是它的逆元了
代码也很简单:
```c++
typedef long long LL;
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
int main() {
int a, p;
cin >> a >> p;
LL x = qmi(a, p - 2, p); // x为a在mod p意义下的逆元
return 0;
}
```
#### 2、欧拉定理求逆元(相当于费马小定理的扩展)
解法一的拓展:欧拉定理+快速幂求逆元($p$不一定为质数,但$a$ $p$还是要互质)
一般不用这种办法,因为它能干的,扩展欧几里得都能干!
<font color='red'><b>略</b></font>
#### 3、扩展欧几里得
**注意:($p$不用为质数但$a,p$要互质,不然$a^{-1}$无解)**
这个方法十分容易理解,而且对于单个查找效率似乎也还不错,比后面要介绍的大部分方法都要快(尤其对于 $mod \ p$ 比较大的时候)。
这个就是利用拓欧求解 线性同余方程 $a∗x≡c(mod \ b)$ 的$c=1$的情况。我们就可以转化为解 $a∗x+b∗y=1$ 这个方程。
求解这个方程的解。不会拓欧可以点[这里](https://www.cnblogs.com/zjp-shadow/p/9267675.html#autoid-3-3-0)~
而且这个做法还有个好处在于,当 $a⊥p$ (互质),但 $p$ 不是质数的时候也可以使用。
代码比较简单:
```c++
void exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) x = 1, y = 0;
else exgcd(b, a % b, y, x), y -= a / b * x;
}
int main() {
LL x, y;
exgcd (a, p, x, y);
x = (x % p + p) % p;
printf ("%d\n", x); //x是a在mod p下的逆元
}
```
#### 4、递推打表
用于求一连串数字对于一个$mod \ p$的逆元。 [P3811 【模板】乘法逆元](https://www.luogu.com.cn/problem/P3811)
对于一个区间的逆元,只能用这种方法,别的算法都比这个要慢。
$O(n)$筛$1$到$n$所有数模$p$的逆元:
* 初值$\large Inv[1]=1$
* 设$\large p=ki+r$,则 $\large ki+r \equiv 0 (mod \ p)$
* 两边同乘$\large i^{-1}r^{-1}$,得$\large kr^{-1}+i^{-1} \equiv 0 (mod \ p)$
* 移项:$\large i^{-1} \equiv -kr^{-1} (mod \ \ p )$
* $\large k=\left \lfloor \frac{p}{i} \right \rfloor$,所以$-k \equiv p- \frac{p}{i} (mod \ \ p ) $
* 所以`Inv[i]=(p-p/i)*Inv[p%i]%p`
### 三、阶乘逆元 [挖坑待填]
线性求阶乘的逆元
设第$x$项阶乘为$x!$,其逆元为$x!^{-1}$,那么有递推式:$\large \displaystyle (x-1)^{-1} = (x!)^{-1}*x$
所以从后往前递推求。
https://blog.csdn.net/qq_52142857/article/details/121032531
https://www.bbsmax.com/A/xl56Pb97zr/
https://blog.csdn.net/kiwi_berrys/article/details/54834977
https://www.freesion.com/article/43361201366/#4__191

View File

@@ -0,0 +1,75 @@
### 模是质数求逆元
如果$a \times b=1(mod\ p)$,则$a$为$b$互为$mod$ $p$逆元,其中$p$是质数。
根据费马小定理得
$$\large a^{p-1}\equiv 1 (mod \ p)$$
$$\large \Rightarrow a \times a^{p-2} \equiv 1 (mod \ \ p )$$
所以,$a$和$a^{p-2}$互为 $(mod \ \ p )$ 逆元。
为了理解快速幂思想,先看一个例子:求$5 ^ {19} = ?$
因为$$19_{(10)} = 10011_{(2)}$$,所以有$$5 ^ {19} = 5 ^ {(1+2+16)} = 5^1 * 5^2 * 5^{16}$$,将二进制分解思想实现。
### 快速幂代码
```c++
#include <bits/stdc++.h>
using namespace std;
int normal_pow(int a, int p) {
int ans = pow(a, p); //普通幂
return ans;
}
//递归版本的快速幂
int qpow_1(int a, int p) {
if (p == 0)
return 1; //任何数的0次方都是1,递归出口
else if (p & 1) //如果p是奇数
return qpow_1(a, p - 1) * a; //结果=qpow_1(a,p-1)*a,相当于用小一级的数字进行描述,形成递归
else {
int t = qpow_1(a, p / 2); //如果p是偶数,分治,结果相乘
return t * t;
}
}
//循环版本的快速幂,利用二进制思想进行8 4 2 1方式计算,达到logN的时间复杂度
int qpow_2(int a, int p) {
int ans = 1;
while (p) {
if (p & 1) ans *= a;
a *= a;
p >>= 1; //缩小一半
}
return ans;
}
int main() {
for (int i = 1; i <= 10; i++) {
cout << normal_pow(2, i) << endl;
cout << qpow_1(2, i) << endl;
cout << qpow_2(2, i) << endl;
}
return 0;
}
```
### 数论题目中的快速幂
```c++
typedef long long LL;
// 快速幂 (a^k)%p
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
```
### 快速幂求逆元
$normal_pow$是普通的求幂,$qpow\_1$是递归方式实现快速幂,$qpow\_2$用循环的方式实现快速幂,求逆元即求$qpow\_1(a, p-2)$或$qpow\_2(a, p-2)$

View File

@@ -0,0 +1,123 @@
## 思路
### 一、前置知识
#### 1、什么是逆元
#### 2、为什么要创造逆元的概念
### 二、乘法逆元
$A$除以一个数模$P$,等于$A$乘以这个数的乘法逆元模$P$。
$(A/B)\% 9937 = (A*B^{-1}) \%9973$. 这里的$B^{-1}$ 代表乘法逆元,而不是$\frac{1}{B}$.
所以我们只需要求解出$B$对于$9973$的 <font color='red'><b>乘法逆元$B^{-1}$</b></font> 即可。
### 三、解法一:费马小定理求解乘法逆元
<font color='red'><b>只适用于模数为质数的情况</b></font>
若$a$是一个整数(除法分母),$b$是一个质数(模),那么 $a^b ≡ a(mod \ b)$ 或 $a^{b-1}≡1 (mod\ b)$
由扩展欧几里得可以得出:
因为$gcd(a,b) = 1, ax ≡ 1(mod \ b)$ $x$是**乘法逆元**
$a^{b-1} = a*a^{b-2}= ax ≡1(mod \ b)$. 所以 $x = a^{b-2}$就是乘法逆元。 可以通过快速幂求解。
在本题求$x = a^{9973-2}$。
```c++
// https://vjudge.csgrandeur.cn/problem/HDU-1576
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/*
测试用例
2
1000 53
87 123456789
答案
7922
6060
总结:
1、如果使用费马小定理+快速幂求逆元,一定要注意使用LL,防止爆int。
*/
const int MOD = 9973;
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p; //如果res是int,可能会越界
k >>= 1;
a = a * a % p; //如果a是int, 可能会越界
}
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
LL a, b;
cin >> a >> b;
LL x;
x = qmi(b, MOD - 2, MOD); // b理解为分母,同余MOD的逆元,变除法为乘法
LL res = a * x % MOD; // a* b在模MOD下的逆元 x
printf("%lld\n", res);
}
return 0;
}
```
### 四、解法二:扩展欧几里得
<font color='red'><b>根据 扩展欧几里得 : 模数可以 不为质数</b></font>
因为$gcd(B,9973) = 1$,所以 $Bx ≡ 1(mod \ 9973)$, $x$ 是 $B^{-1}$,也就是$B$的乘法逆元
通过`ex_gcd(B,9973)`求出来的`x`就是$B^{-1}$,因为求出来的$x$可能是负数,利用通解所以处理一下
```C++
x = (x % t + t) % t;
```
实现代码
```c++
// https://vjudge.csgrandeur.cn/problem/HDU-1576
#include <bits/stdc++.h>
typedef long long LL;
/*
测试用例
2
1000 53
87 123456789
答案
7922
6060
*/
using namespace std;
const int MOD = 9973;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int t;
cin >> t;
while (t--) {
LL x, y;
LL a, b;
cin >> a >> b;
exgcd(b, MOD, x, y);
x = (x % MOD + MOD) % MOD; //对比使用费马小定理,这里对于扩展欧几里得算出的逆元,还需要进一步模一下 MOD才行,否则可能是负的
cout << (x * a) % MOD << endl;
}
return 0;
}
```

View File

@@ -0,0 +1,31 @@
// https://blog.csdn.net/kiwi_berrys/article/details/54834977
// https://www.cnblogs.com/zjp-shadow/p/7773566.html
//阶乘逆元
#define ll long long
ll fac[1000000 + 5]; //阶乘
ll inv[1000000 + 5]; //逆元
void getfac() {
fac[0] = inv[0] = 1;
for (int i = 1; i <= 1000000; i++) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = quick_power(fac[i], mod - 2);
//表示i的阶乘的逆元
}
}
//组合数
inline ll getC(ll n, ll m) // C(n,m) = n!/((n-m)!*m!) % mod
{
return fac[n] * inv[n - m] % mod * inv[m] % mod;
}
//当 n,m 过大时,可以用Lucas定理降数据
inline ll Lucas(ll n, ll m) {
if (n < mod && m < mod) return getC(n, m);
return Lucas(n / mod, m / mod) * getC(n % mod, m % mod) % mod;
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
//排列数
inline ll
getA(ll n, ll m) // A(n,m) = n!/(n-m)! % mod
{
return n * inv[n - m] % mod;
}