文章目录
- 作用
- 证明
- AcWing 204. 表达整数的奇怪方式
- CODE
作用
用于求模数两两互质的线性同余方程组,若不互质则不存在解。
《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?这就是经典的剩余定理问题,也是我们小学题目:三个三个数余二,五个五个数余三,七个七个数余二,求这个数是几? { x ≡ 2 ( m o d 3 ) x ≡ 3 ( m o d 5 ) x ≡ 2 ( m o d 7 ) \left\{ \begin{array}{c} x ≡ 2\ (mod\ 3)\\ x ≡ 3\ (mod\ 5)\\ x ≡ 2\ (mod\ 7) \end{array} \right. ⎩ ⎨ ⎧x≡2 (mod 3)x≡3 (mod 5)x≡2 (mod 7)
更多详细介绍请看VCR: 中国剩余定理的由来与求解过程。
证明
请看题解:https://www.acwing.com/solution/content/3539/ 和https://www.acwing.com/solution/content/23099/
思路就是:先读入一个式子,然后以这个式子为基准,再读入一个式子,找他们的通解,将式子更新为他们的通解,然后再读入,继续找通解,直到读完。
证明过程太纷繁复杂,蒟蒻的我选择直接记公式:
-
每次更新完,式子变为: x = k ∗ l c m ( a 1 , a 2 ) + k 1 ∗ a 1 + m 1 = k ∗ a 0 + m 0 x = k * lcm(a_1, a_2) + k_1 * a_1 + m_1 = k * a_0 + m_0 x=k∗lcm(a1,a2)+k1∗a1+m1=k∗a0+m0
所以我们需要更新: a 1 = l c m ( a 1 , a 2 ) = a 1 / d ∗ a 2 m 1 = k 1 ∗ a 1 + m 1 a_1 = lcm(a_1, a_2) = a_1 / d * a_2\\ m_1 = k_1 * a_1 + m_1 a1=lcm(a1,a2)=a1/d∗a2m1=k1∗a1+m1
在这之前我们需要将 k 1 k_1 k1 的值更新出来: k 1 = ( ( m 2 − m 1 ) / d ∗ k 1 ) % ( a 2 / d ) k_1 = ((m2 - m1) / d * k_1) \%\ (a2 / d) k1=((m2−m1)/d∗k1)% (a2/d) 就是求最小公倍数,然后取模求最小解。到最后,我们求的 m 1 m_1 m1 即为 x x x,求个模找最小即可。
AcWing 204. 表达整数的奇怪方式
题目链接:https://www.acwing.com/activity/content/problem/content/948/
CODE
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>using namespace std;typedef long long ll; // 定义长整型别名为ll// 扩展欧几里得算法,求解ax + by = gcd(a, b)的一组解
ll exgcd(ll a, ll b, ll &x, ll &y){if(b == 0){ // 当b为0时,x为1,y为0x = 1, y = 0;return a; // 返回最大公约数}ll x1, y1;ll d = exgcd(b, a % b, x1, y1); // 递归求解x = y1, y = x1 - a / b * y1; // 更新x和yreturn d; // 返回最大公约数
}int main(){int n; // 定义整型变量nscanf("%d", &n); // 输入nll a1, m1, x = 0; // 定义长整型变量a1, m1, x,并初始化x为0cin >> a1 >> m1; // 输入a1和m1for(int i = 0; i < n - 1; ++i){ll a2, m2, k1, k2; // 定义长整型变量a2, m2, k1, k2cin >> a2 >> m2; // 输入a2和m2ll d = exgcd(a1, a2, k1, k2); // 调用扩展欧几里得算法求解最大公约数if((m1 - m2) % d){ // 如果(m1 - m2)不能被d整除x = -1; // x赋值为-1break; // 跳出循环}k1 = k1 * (m2 - m1) / d; // 更新k1ll t = abs(a2 / d); // 定义长整型变量t,并赋值为a2/d的绝对值k1 = (k1 % t + t) % t; // 更新k1m1 = k1 * a1 + m1; // 更新m1a1 = abs(a1 * a2 / d); // 更新a1}if(x != -1) x = (m1 % a1 + a1) % a1; // 如果x不等于-1,则更新xcout << x << endl; // 输出x
}
真的nm太抽象了吧,这玩意儿是人学的?我选择直接背,以我的水平考到了也做不出来。