数据结构与算法学习笔记----中国剩余定理

数据结构与算法学习笔记----中国剩余定理

@@ author: 明月清了个风

@@ first publish time: 2025.1.17

ps⭐️一道中国剩余定理的扩展题,在做这道题之前了解之前的逆元和扩展欧几里得算法能够更好的帮助理解(因为中间涉及求逆的运算)

Acwing 204. 表达整数的奇怪方式

原题链接\]([204. 表达整数的奇怪方式 - AcWing题库](https://www.acwing.com/problem/content/206/)) 给定 2 n 2n 2n个整数 a 1 , a 2 , ⋯   , a n a_1,a_2,\\cdots,a_n a1,a2,⋯,an和 m 1 , m 2 , ⋯   , m n m_1,m_2,\\cdots,m_n m1,m2,⋯,mn,求一个最小的非负整数 x x x,满足 ∀ i ∈ \[ 1 , n \] , x ≡ m i ( m o d a i ) \\forall i \\in\[1,n\], x \\equiv m_i \\pmod{a_i} ∀i∈\[1,n\],x≡mi(modai)。 #### 输入格式 第一行包含整数 n n n, 第 2 , ⋯   , n + 1 2,\\cdots,n+1 2,⋯,n+1行:每 i + 1 i+1 i+1行包含两个整数 a i a_i ai和 m i m_i mi,数之间用空格隔开。 #### 输出格式 输出最小非负整数 x x x,如果 x x x不存在,则输出 − 1 -1 −1。 #### 数据范围 1 ≤ a i ≤ 2 3 1 − 1 1 \\le a_i \\le 2\^31 - 1 1≤ai≤231−1, 0 ≤ m i ≤ a i 0 \\le m_i \\le a_i 0≤mi≤ai, 1 ≤ n ≤ 25 1 \\le n \\le 25 1≤n≤25 所有 m i m_i mi的最小公倍数在 64 64 64位有符号整数范围内。 #### 思路 **中国剩余定理**其实是一个公式,我们先给出这个定理: **中国剩余定理**,又称孙子定理,是中国古代求解一次同余式组的方法,也是数论中的一个重要定理。它最早记载于中国南北朝时期(公元5世纪)的数学著作《孙子算经》卷下第二十六题,题为"物不知数"问题,原文如下: > 有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何? 用现代数学语言描述,即求解满足以下同余方程组的整数 x x x: { x ≡ 2 ( m o d 3 ) x ≡ 3 ( m o d 5 ) x ≡ 2 ( m o d 7 ) \\begin{cases} x \\equiv 2 \\pmod{3} \\\\ x \\equiv 3 \\pmod{5} \\\\ x \\equiv 2 \\pmod{7} \\end{cases} ⎩ ⎨ ⎧x≡2(mod3)x≡3(mod5)x≡2(mod7) #### 定理内容 > 设 m 1 , m 2 , ... , m k m_1, m_2, \\ldots, m_k m1,m2,...,mk 是 k k k 个两两互质的正整数,对于任意的整数 a 1 , a 2 , ... , a k a_1, a_2, \\ldots, a_k a1,a2,...,ak,同余式组 > { x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋮ x ≡ a k ( m o d m k ) (1) \\begin{cases} x \\equiv a_1 \\pmod{m_1} \\\\ x \\equiv a_2 \\pmod{m_2} \\\\ \\vdots \\\\ x \\equiv a_k \\pmod{m_k} \\tag{1} \\end{cases} ⎩ ⎨ ⎧x≡a1(modm1)x≡a2(modm2)⋮x≡ak(modmk)(1) > > 有唯一解,解的形式为: > x ≡ M 1 ′ M 1 a 1 + M 2 ′ M 2 a 2 + ... + M k ′ M k a k ( m o d M ) (2) x \\equiv M_1' M_1 a_1 + M_2' M_2 a_2 + \\ldots + M_k' M_k a_k \\pmod{M} \\tag{2} x≡M1′M1a1+M2′M2a2+...+Mk′Mkak(modM)(2) > > 其中, M = m 1 × m 2 × ... × m k M = m_1 \\times m_2 \\times \\ldots \\times m_k M=m1×m2×...×mk, M i = M m i M_i = \\frac{M}{m_i} Mi=miM, M i ′ M i ≡ 1 ( m o d m i ) M_i' M_i \\equiv 1 \\pmod{m_i} Mi′Mi≡1(modmi)(即 M i ′ M_i' Mi′ 是 M i M_i Mi 关于模 m i m_i mi 的逆元)。 定理中有一个很强的限制条件就是这些 m i m_i mi需要两两互质,但是我们的题干中并没有这个条件,因此需要进行一些推导 我们先从将题目简化看是否能够理出思路,假设有以下方程组需要求解 { x m o d    a 1 = m 1 x m o d    a 2 = m 2 (3) \\begin{cases} x \\mod a_1 = m_1 \\\\ x \\mod a_2 = m_2 \\\\ \\tag{3} \\end{cases} {xmoda1=m1xmoda2=m2(3) 那么就有下面的等式: { x = k 1 ⋅ a 1 + m 1 x = k 2 ⋅ a 2 + m 2 (4) \\begin{cases} x = k_1 \\cdot a_1 + m_1 \\\\ x = k_2 \\cdot a_2 + m_2 \\\\ \\tag{4} \\end{cases} {x=k1⋅a1+m1x=k2⋅a2+m2(4) 利用式(5)中的等式就可以得到下面的式子: k 1 ⋅ a 1 + m 1 = k 2 ⋅ a 2 + m 2 (5) k_1 \\cdot a_1 + m_1 = k2 \\cdot a_2 + m_2 \\tag{5} k1⋅a1+m1=k2⋅a2+m2(5) 移项可得: k 1 ⋅ a 1 − k 2 ⋅ a 2 = m 2 − m 1 (6) k_1 \\cdot a_1 - k_2 \\cdot a_2 = m_2 - m_1 \\tag{6} k1⋅a1−k2⋅a2=m2−m1(6) 这里就是我们之前用扩展欧几里得算法求解的方程了,上式有解需要满足 m 2 − m 1 m_2 - m_1 m2−m1能够整除 gcd ⁡ ( a 1 , a 2 ) \\gcd(a_1,a_2) gcd(a1,a2),求出 k 1 , k 2 k_1,k_2 k1,k2后即可得 x x x的值。 那么这里式(6)就可以看成是一个一次不定方程,我们假设其有解,即上述有解条件成立,那么就可以使用**扩展欧几里得算法** 找到一组特解 ( k 1 ′ , k 2 ′ ) (k_1',k_2') (k1′,k2′),其通解形式就可以表示为 { k 1 = k 1 ′ + k a 2 g c d ( a 1 , a 2 ) k 2 = k 2 ′ + k a 1 g c d ( a 1 , a 2 ) (7) \\begin{cases} k_1 = k_1' + k\\frac{a_2}{gcd(a_1,a_2)} \\\\ k_2 = k_2' + k\\frac{a_1}{gcd(a_1,a_2)} \\\\ \\tag{7} \\end{cases} {k1=k1′+kgcd(a1,a2)a2k2=k2′+kgcd(a1,a2)a1(7) 那么再将上述结果带入式(5)即可得到 x x x的所有解的形式,这里将 gcd ⁡ ( a 1 , a 2 ) \\gcd(a_1,a_2) gcd(a1,a2)记为 d d d x = k 1 ⋅ a 1 + m 1 = ( k 1 ′ + k a 2 d ) a 1 + m 1 = a 1 k 1 + m 1 + k a 1 a 2 d = a 1 k 1 + m 1 + k ⋅ \[ a 1 , a 2 \] \\begin{equation} \\begin{aligned} x \& = k_1 \\cdot a_1 + m_1 \\\\ \& = (k_1' + k\\frac{a_2}{d}) a_1+m_1 \\\\ \& = a_1k_1 + m_1 + k \\frac{a_1 a_2}{d} \\\\ \& = a_1k_1 + m_1 + k \\cdot \[a_1,a_2\] \\\\ \\tag{8} \\end{aligned} \\end{equation} x=k1⋅a1+m1=(k1′+kda2)a1+m1=a1k1+m1+kda1a2=a1k1+m1+k⋅\[a1,a2\](8) 这里将 a 1 k 1 + m 1 a_1k_1 + m_1 a1k1+m1记为新 x 0 x_0 x0,将 k ⋅ \[ a 1 , a 2 \] k \\cdot \[a_1,a_2\] k⋅\[a1,a2\]记为新的 a a a,则有 x = x 0 + k a (9) x = x_0 + ka \\tag{9} x=x0+ka(9) 也就是说我们可以将式(5)的两个不定等式变为式(10),每两个式(4)所示的方程可以缩减一次,那么 n − 1 n-1 n−1次之后就可以只有一个方程了,而上面这个方程其实就是求一个最小正整数解 x x x满足,到这就可以开始写代码啦,代码中也给出了一些注释 x   m o d   a ≡ x 0 (10) x \\bmod a \\equiv x_0 \\tag{10} xmoda≡x0(10) #### 代码 ```cpp #include #include using namespace std; typedef long long LL; // 需要long long,因为我们会求最多25个数的最小公倍数 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 n; cin >> n; LL a1, m1; cin >> a1 >> m1; bool flag = true; for(int i = 0; i < n - 1; i ++) // 按照我们推导的过程,每两个方程进行一次合并 { LL a2, m2; cin >> a2 >> m2; LL k1, k2; LL d = exgcd(a1, a2, k1, k2); if((m2 - m1) % d) // 扩展欧几里得算法有解的条件,找一下上文中高亮部分 { flag = false; break; } k1 *= (m2 - m1) / d; // 这里还是扩展欧几里得的内容,我们算的等式右边是(m2-m1)的最大公约数,系数要乘回去 LL t = a2 / d; k1 = (k1 % t + t) % t; // 这里的操作是取一个最小的k,防止爆int m1 = a1 * k1 + m1; // 这里就是上面的x0 a1 = abs(a1 / d * a2); // 这里就是上面的[a1,a2] } if(flag) { cout << (m1 % a1 + a1) % a1 <

相关推荐
honey ball17 分钟前
参考平面跨分割情况下的信号回流
学习
_yingty_30 分钟前
GO语言入门经典-反射3(Value 与对象的值)
开发语言·前端·后端·学习·golang
林麓1 小时前
C++进阶笔记第二篇:引用
笔记
茕离1 小时前
供应链建模大师相关操作笔记——报错可能原因
笔记
ld8512 小时前
《微服务与事件驱动架构》读书分享
笔记
郭涤生3 小时前
第二章:Docker及Kubernetes基础 重难点详解_《再也不踩坑的kubernetes实战指南》
笔记·docker·容器·kubernetes
李匠20243 小时前
C++学习之服务器EPOLL模型、处理客户端请求、向客户端回复数、向客户端发送文件
运维·服务器·c++·学习
浪淘沙jkp3 小时前
大模型学习六:‌小米8闲置,通过Termux安装ubuntu做个随身服务器,纯粹碍眼折腾
学习·ubuntu·termux
虾球xz3 小时前
游戏引擎学习第210天
学习·游戏引擎
奋斗的牛马3 小时前
FPAG_BUFFER学习
学习·fpga开发