注:做法和思路是 zhx 在一次讲课中提出的,如有侵权,请联系作者删除

其实别的题解也有提到过暴力做法,但这里将会给出更加严谨的复杂度的证明

正文

引入

我们知道,中国剩余定理是一种用来求解类似于

\[\begin{cases}
x \equiv a_1 \pmod {m_1} \\
x \equiv a_2 \pmod {m_2} \\
x \equiv a_3 \pmod {m_3} \\
... \\
x \equiv a_4 \pmod {m_4} \\
\end{cases}
\]

形式的同余方程组的定理,要求我们找出 \(x\) 的最小非负整数解

大数翻倍法

现在市面上比较推广的一种方法是用扩展欧几里得来求解同余方程组。

这里将介绍一种更为暴力的算法——大数翻倍法,写起来也更加方便简洁。

先来考虑两个同余方程的情况:

\[\begin{cases}
x \equiv a_1 \pmod {m_1} \\
x \equiv a_2 \pmod {m_2}
\end{cases}
\]

考虑用一种暴力的方法将其合并成一个同余方程。让我们设初始的 \(x = 0, m = 1\),合并了第一个方程后变为 \(x = a_1, m = m_1\)。

那么现在只需要满足第二个同余方程即可。我们知道 \((a_1 + km_1) \mod m_1 = a_1\),一个显然的想法是每次暴力的加 \(m_1\),然后暴力的判断能否满足第二个同余方程。找到一个能满足的情况合并即可,模数合并为 $ \operatorname{lcm}(m_1,m_2)$,代码也十分好写,只有四行:

void Merge(LL &a1, LL &m1, LL a2, LL m2) {
while(a1 % m2 != a2) a1 += m1;
m1 = Lcm(m1, m2);
}

复杂度证明

根据费马小定理我们知道 \(a^{p-1} \equiv 1 \pmod p\) ,

又因为 \(a^0 \equiv 1 \pmod p\) ,所以得到

\[a^{p-1} \equiv a^0 \pmod p
\]

将其推广就会有:

\[a^{x + p-1} \equiv a^x \pmod p
\]

这说明了什么?

\(a^x\) 在模 \(p\) 下的循环节,在最坏情况下只有 \(p-1\) 大小。

所以上面代码每次合并的复杂度是 \(O(m_2)\) 的。发现更小的模数的复杂度更优,所以我们添一句优化,通过特判转换一下枚举的模数即可。代码改为:

void Merge(LL &a1, LL &m1, LL a2, LL m2) {
if(m1 < m2) swap(m1, m2), swap(a1, a2);
while(a1 % m2 != a2) a1 += m1;
m1 = Lcm(m1, m2);
}

所以总的复杂度为 \(O(\sum_{i=1}^{n}m_i)\)。

但是!它的复杂度真的有那么高吗?(那我也没必要写这篇博客了是吧

我们知道答案一定在 long long 范围内,并且 \(\prod_{i=1}^{n} m_i\) 一定也不会爆 long long

因为高精度求解同余方程组也没那个做法是吧,出题人也一定不会出个爆 long long 的样例,因为他自己也做不了。

让我们来考虑最坏情况:

想要卡我们,每个模数都得是一个大质数。还要保证成绩和在 long long 范围内(也就是 \(10^{18}\))。

那么只有一种情况, \(n = 2\)!此时 \(m_i\) 可以做到 \(2 \times 10^9\) 级别的大质数。总时间复杂度为 \(O(10^9)\) ,可以被卡。

但是,当 \(n = 3\) 时, \(m_i\) 只有 \(10^6\) 级别,我们的复杂度也只有 \(O(3 \times 10^6)\) ,可以通过。

\(n\) 更大的情况就不必说了吧。

大数翻倍法的优势

  • 码量小
  • 理解难度小
  • 一般不会被卡,没有人会对着这个非主流算法卡十个点的
  • 不需要考虑模数互质的情况

最后的最后:上代码!

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 大数翻倍法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
LL n, v, d, a, b;
LL read(){
LL s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
} LL Gcd(LL x, LL y) { return !y ? x : Gcd(y, x % y); }
LL Lcm(LL x, LL y) { return x / Gcd(x, y) * y; }
void Merge(LL &a1, LL &m1, LL a2, LL m2) {
if(m1 < m2) swap(m1, m2), swap(a1, a2);
while(a1 % m2 != a2) a1 += m1;
m1 = Lcm(m1, m2);
} int main()
{
n = read(); v = 0, d = 1; // 初始化
for(int i = 1; i <= n; ++i) a = read(), b = read(), b %= a, Merge(v, d, b, a);
printf("%lld", v);
return 0;
}

如果觉得写的不错就点个赞吧这个做法顶上去吧/kel

大数翻倍法求解CRT的更多相关文章

  1. 破圈法求解最小生成树c语言实现(已验证)

    破圈法求解最小生成树c语言实现(已验证) 下面是算法伪代码,每一个算法都取一个图作为输入,并返回一个边集T. 对该算法,证明T是一棵最小生成树,或者证明T不是一棵最小生成树.此外,对于每个算法,无论它 ...

  2. Elastic Search 上市了,市值翻倍,这群人财务自由了!

    国庆长假,大部分人还深浸在风花雪月之中,而就在昨天(美国时间10月5号),我们 Java 程序员所熟知的大名鼎鼎的 Elastic Search 居然在美国纽约证券交易所上市了! 当说到搜索时,大部分 ...

  3. VS Code:让你工作效率翻倍的23个插件和23个编辑技巧

    VS Code:让你工作效率翻倍的23个插件和23个编辑技巧 总结了一些平时常用且好用的 VS Code 的插件和编辑技巧分享出来. 文章详情可查阅我的博客:lishaoy.net ,欢迎大家访问. ...

  4. POJ 1061 青蛙的约会(拓展欧几里得算法求解模线性方程组详解)

    题目链接: BZOJ: https://www.lydsy.com/JudgeOnline/problem.php?id=1477 POJ: https://cn.vjudge.net/problem ...

  5. 优步UBER司机奖励政策:含高峰、翻倍、行程、金牌司机、保底奖励(持续更新...)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://didi-uber.com/archiv ...

  6. Coursera在线学习---第一节.梯度下降法与正规方程法求解模型参数比较

    一.梯度下降法 优点:即使特征变量的维度n很大,该方法依然很有效 缺点:1)需要选择学习速率α 2)需要多次迭代 二.正规方程法(Normal Equation) 该方法可以一次性求解参数Θ 优点:1 ...

  7. 北京优步UBER司机B组最新奖励政策、高峰翻倍奖励、行程奖励、金牌司机奖励【每周更新】

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  8. 逆波兰法求解数学表达示(C++)

    主要是栈的应用,里面有两个函数deleteSpace(),stringToDouble()在我还有一篇博客其中:对string的一些扩展函数. 本程序仅仅是主要的功能实现,没有差错控制. #inclu ...

  9. 0-1背包问题——回溯法求解【Python】

    回溯法求解0-1背包问题: 问题:背包大小 w,物品个数 n,每个物品的重量与价值分别对应 w[i] 与 v[i],求放入背包中物品的总价值最大. 回溯法核心:能进则进,进不了则换,换不了则退.(按照 ...

随机推荐

  1. Linux安装jdk(两种方式)

    最近在研究大数据方面的东西,业务场景是从设备采集数据经过处理然后存放DB. 建设上面的环境第一步肯定是安装jdk,所以和大家一起学一下基本知识centos7.5安装jdk1.8. 安装jdk有两种方法 ...

  2. python自动化测试生成HTML报告

    自动化测试结果执行完毕后,需要对测试结果进行查看,今天就来讲一讲如何生成HTML报告首先下载HTMLTestRunner.py文件,放在python安装目录的Lib文件夹下https://pan.ba ...

  3. webstorm2020.3安装破解教程

    免责声明:本教程及相关附件仅限于学术交流,不能用于商业以及违法用途,请于下载后24小时内删除!如产生法律纠纷,一切与本人无关,呼吁各位小伙伴支持下正版软件.本文如有侵权,请联系小编删除之. 该操作是用 ...

  4. 02----python入门----基本数据类型

    关于数据分类依据 一.数字型(int) Python可以处理任意大小的正负整数,但是实际中跟我们计算机的内存有关,在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,在64位系 ...

  5. ModuleNotFoundError: No module named 'django'

    1 .就在前天 我还能用python3 manage.py runserver 0.0.0.0:8000 启动Django  今天就突然报错了(心情极为复杂,你这也能信?)   2.打印python找 ...

  6. C# 应用 - 使用 WepApp 处理文件上传、下载请求

    1. 代码 /// <summary> /// 文件上传下载控制器 /// </summary> public class FileController : ApiContro ...

  7. httpd解析PHP

    1.vim /usr/local/apache2.4/conf/httpd.conf 编辑httpd的主配置文件 搜索ServerName,把ServerName www.example.com:80 ...

  8. OpenCV 之 角点检测

    角点 (corners) 的定义有两个版本:一是 两条边缘的交点,二是 邻域内具有两个主方向的特征点. 一般而言,角点是边缘曲线上曲率为极大值的点,或者 图像亮度发生剧烈变化的点.例如,从人眼角度来看 ...

  9. [ONTAK2010] Peaks 加强版

    [ONTAK2010] Peaks 加强版 题目大意:原题变为强制在线查询 Solution 读入山高,排序后依然建立树链,初始化并查集,初始化重构树新节点标号为\(n+1\) 读入边,按照边权从小到 ...

  10. AmazonS3 使用AWS SDK for Java实现跨源资源共享 (CORS)

    CORS 配置 创建 CORS 配置并对存储桶设置该配置 通过添加规则来检索并修改配置 向存储桶添加修改过的配置 删除配置 import com.amazonaws.AmazonServiceExce ...