信奥赛C++提高组csp-s之数论基础专题课:中国剩余定理2(编程案例实践1)

信奥赛C++提高组csp-s之数论基础专题课:中国剩余定理2(编程案例实践1)

中国剩余定理(CRT)是数论中的一个重要定理,在信奥赛(NOI系列赛事)中属于必须掌握的模板级别知识。它主要用于求解一元线性同余方程组。

前面我们从数学原理手算例子 进行了详细讲解,接下来我们进行编程实战,先举第一个案例。


中国剩余定理(CRT)/ 曹冲养猪

题目描述

自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲满不高兴,于是在工作中马马虎虎,有一次曹操想知道母猪的数量,于是曹冲想狠狠耍曹操一把。举个例子,假如有 16 16 16 头母猪,如果建了 3 3 3 个猪圈,剩下 1 1 1 头猪就没有地方安家了。如果建造了 5 5 5 个猪圈,但是仍然有 1 1 1 头猪没有地方去,然后如果建造了 7 7 7 个猪圈,还有 2 2 2 头没有地方去。你作为曹总的私人秘书理所当然要将准确的猪数报给曹总,你该怎么办?

输入格式

第一行包含一个整数 n n n ------ 建立猪圈的次数,接下来 n n n 行,每行两个整数 a i , b i a_i, b_i ai,bi,表示建立了 a i a_i ai 个猪圈,有 b i b_i bi 头猪没有去处。你可以假定 a 1 ∼ a n a_1 \sim a_n a1∼an 互质。

输出格式

输出包含一个自然数,即为曹冲至少养母猪的数目。

输入输出样例 1
输入 1
复制代码
3
3 1
5 1
7 2
输出 1
复制代码
16
说明/提示

1 ≤ n ≤ 10 1 \leq n\le10 1≤n≤10, 0 ≤ b i < a i ≤ 100000 0 \leq b_i\lt a_i\le100000 0≤bi<ai≤100000, 1 ≤ ∏ a i ≤ 10 18 1 \leq \prod a_i \leq 10^{18} 1≤∏ai≤1018

思路分析

本题是**中国剩余定理(CRT)**的模板题。

题目给出 n n n 个同余方程:
x ≡ b i ( m o d a i ) ( i = 1 , ... , n ) x \equiv b_i \pmod{a_i} \quad (i=1,\dots,n) x≡bi(modai)(i=1,...,n)

其中 a i a_i ai 两两互质,要求最小的非负整数解 x x x。

中国剩余定理

设 M = ∏ i = 1 n a i M = \prod_{i=1}^{n} a_i M=∏i=1nai, M i = M / a i M_i = M / a_i Mi=M/ai, t i t_i ti 是 M i M_i Mi 在模 a i a_i ai 下的乘法逆元(即 M i ⋅ t i ≡ 1 ( m o d a i ) M_i \cdot t_i \equiv 1 \pmod{a_i} Mi⋅ti≡1(modai))。

则方程组的解为:
x ≡ ∑ i = 1 n b i ⋅ M i ⋅ t i ( m o d M ) x \equiv \sum_{i=1}^{n} b_i \cdot M_i \cdot t_i \pmod{M} x≡∑i=1nbi⋅Mi⋅ti(modM)

最小非负解即为 x   m o d   M x \bmod M xmodM。

注意点

  • 由于 ∏ a i ≤ 10 18 \prod a_i \le 10^{18} ∏ai≤1018, M M M 在 long long 范围内,但计算 b i ⋅ M i ⋅ t i b_i \cdot M_i \cdot t_i bi⋅Mi⋅ti 时可能超过 long long,需要使用 __int128 进行乘法以避免溢出。
  • 逆元通过扩展欧几里得算法 求解,因为 M i M_i Mi 与 a i a_i ai 互质,逆元一定存在。
  • 最后答案取模 M M M 后输出(保证非负)。

代码实现

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

// 扩展欧几里得:求 ax + by = gcd(a,b) 的一组解,返回 gcd
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) { x = 1; y = 0; return a; }
    ll g = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return g;
}

int main() {
    int n; cin >> n;
    ll a[15], b[15], M = 1;   // a:模数, b:余数, M:总乘积
    for (int i = 1; i <= n; ++i) {
        cin >> a[i] >> b[i];
        M *= a[i];             // 累乘得到 M,题目保证 M ≤ 1e18
    }

    ll ans = 0;
    for (int i = 1; i <= n; ++i) {
        ll Mi = M / a[i];       // Mi = M / a[i]
        ll ti, y;                // ti 是 Mi 模 a[i] 的逆元
        exgcd(Mi, a[i], ti, y);  // 解 Mi*ti + a[i]*y = 1,得到 ti
        ti = (ti % a[i] + a[i]) % a[i]; // 保证 ti 为正

        // 计算 b[i] * Mi % M * ti % M,用 __int128 防止溢出
        ans = (ans + (__int128)b[i] * Mi % M * ti) % M;
    }

    cout << ans << endl;
    return 0;
}

功能分析

  1. 输入处理 :读入 n n n 和每一对 ( a i , b i ) (a_i, b_i) (ai,bi),同时累乘得到 M M M。
  2. 扩展欧几里得函数exgcd 用于求解 M i ⋅ t i + a i ⋅ y = 1 M_i \cdot t_i + a_i \cdot y = 1 Mi⋅ti+ai⋅y=1,从而得到 t i t_i ti(模 a i a_i ai 下的逆元)。
    因为 a i a_i ai 与 M i M_i Mi 互质,所以 gcd ⁡ ( M i , a i ) = 1 \gcd(M_i, a_i) = 1 gcd(Mi,ai)=1,方程必有解,且解出的 t i t_i ti 即为逆元。
  3. 核心计算
    • 对每个方程,计算 M i = M / a i M_i = M / a_i Mi=M/ai。
    • 调用 exgcd 求逆元 t i t_i ti,并调整为最小非负整数。
    • 累加 b i ⋅ M i ⋅ t i b_i \cdot M_i \cdot t_i bi⋅Mi⋅ti 到答案,每一步乘法都使用 __int128 暂存,最后对 M M M 取模。
  4. 输出:输出最终的非负整数解。

更多系列知识,请查看专栏:《信奥赛C++提高组csp-s知识详解及案例实践》:
https://blog.csdn.net/weixin_66461496/category_13113932.html


各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

1、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html

2、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新)
https://blog.csdn.net/weixin_66461496/category_13125089.html

3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html

4、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
乐观勇敢坚强的老彭2 小时前
c++信奥for循环强化03
开发语言·c++
Aawy1202 小时前
C++与Rust交互编程
开发语言·c++·算法
艾莉丝努力练剑2 小时前
System V IPC底层原理详解
linux·运维·服务器·网络·c++·人工智能·学习
落羽的落羽2 小时前
【Linux系统】信号机制拆解,透过内核三张表深入本质
android·java·linux·服务器·c++·spring·机器学习
十年编程老舅2 小时前
Linux 内存爆满?分清泄漏与正常占用
linux·c++·内存·内存管理·内存泄漏·内存溢出
艾莉丝努力练剑2 小时前
【Linux:文件 + 进程】进程间通信进阶(2)
linux·运维·服务器·开发语言·网络·c++·ubuntu
2401_894241922 小时前
C++中的策略模式变体
开发语言·c++·算法
计算机安禾2 小时前
【C语言程序设计】第33篇:二级指针与指针数组
c语言·开发语言·数据结构·c++·算法·visual studio code·visual studio
落地加湿器2 小时前
Acwing算法课图论与搜索笔记
c++·笔记·算法·图论·dfs·bfs·图搜索算法