前置知识

1.

a%b=d,c%b=e,

则(a+c)%b=(d+e)%b(正确性在此不加证明)

2.

a%b=1,则(d\(\times\)a)%b=d%b(正确性在此不加证明)

下面先看一道题(改编自曹冲养猪):

烤绿鸟的故事

题目描述:

mian包是一个贪吃的孩子,这天,他买了一堆绿鸟吃。当然他的妈妈并不想让他吃太多食物(因为那样会发胖),为了避免老妈的唠叨,他决定不告诉他的妈妈绿鸟数量,而是将绿鸟的数量x用以下式子来描述

\[\begin{cases}x≡b_1 (mod a_1)\\x≡b_2 (mod a_2)\\...\\x≡b_n (mod a_n)\end{cases}
\]

(\(a_1\),\(a_2\)......\(a_n\)两两互质)

由于他的妈妈数学不好,于是就来向你求助了,请你求出mian包最少买了多少烤绿鸟

输入输出格式

输入格式:

第一行包含一个整数n (n <= 10) – 告诉妈妈的式子数,接下来n行,每行两个整数ai, bi( bi <= ai <= 1000)

首先,我们把这道题简化成下面的图

样例为:

  1. 3
  2. 3 1
  3. 5 1
  4. 7 2

很显眼吧?怎么做呢?

1.妈妈,我会暴力

这就很简单了

我们从第二个开始枚举

初始值为a1+b1;

每次自加当前的已经满z足的\(a_1\)**\(a_2\)......\(a_{i-1}\)的乘积(为什么我接下来说)

如果当前值已经满足%\(a_i\)=\(b_i\)

则当前已经成立

ps:上式为什么成立呢?

以上面的例子为例,看下面我的图

当已经满足前两个等式时,我们增加\(a_1\)*\(a_2\)

那么我们会增加15只绿鸟

看图:

很显然,15%3=0,15%5=0

所以增加15的话一定满足当前的等式

自然,暴力就解决了

暴力代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. using namespace std;
  5. long long a,b,c,d,e,f,g,kkk,n;
  6. struct zzzz{
  7. int x,y,z;
  8. }ltt[15];
  9. bool cmp(zzzz s,zzzz t)
  10. {
  11. return s.x>t.x;
  12. }
  13. int main()
  14. {
  15. cin>>n;
  16. kkk=1;
  17. for(a=1;a<=n;a++)
  18. {
  19. cin>>ltt[a].x>>ltt[a].y;
  20. }
  21. sort(ltt+1,ltt+n+1,cmp);
  22. kkk=ltt[1].x;
  23. g=ltt[1].y;
  24. f+=2;
  25. while(f<=n)//枚举每个等式
  26. {
  27. while(1)
  28. {
  29. if(g%ltt[f].x==ltt[f].y)
  30. {
  31. kkk=kkk*ltt[f].x;
  32. f++;
  33. break;
  34. }
  35. g=g+kkk;//自加,直到满足当前等式
  36. }
  37. }
  38. cout<<g;
  39. }

诚然,这种方法好想也好写,但是,我们要注意一个问题

看下面这个式子:

\[\begin{cases}x≡1 (mod 3)\\x≡1 (mod 5)\\x≡19260817 (mod 1000000007)\end{cases}
\]

好吧,你自加去吧,保证TLE

这时,我们就需要一个高效的算法

前置知识3:

每一个crt方程组的解在模\(a_1 \times a_2 \times …… \times a_n\)意义下有且只有1个

为什么呢?因为每个解之间的差是所有a的乘积,加数不管对序列中的哪个数取模都是0呗

2.关于暴力的扩展

先说点别的东西

由上面的暴力我们可以看出,该样例crt解法的本质是从5和7的公倍数中找一个除以3余1的数\(n_1\),从3和7的公倍数中找一个除以5余1的数\(n_2\),从3和5的公倍数中找一个除以7余2的数\(n_3\),再将三个数相加得到解。

那么我们可不可以说,更多项的答案也可以这样的得来吗?

答案是肯定的

我们设{\(a_1 \times a_2 \times ...... \times a_n\)}为mul

然后按照暴力的方法,我们可以得到这样一个式子:

\(k_1\times\frac{(mul)}{a1}\)+\(k_2\times\frac{(mul)}{a2}\)+……+\(k_n\times\frac{(mul)}{an}\)

并满足(\(k_i\times\frac{(mul)}{a_i}\)%\(a_i\)=\(b_i\))

正确性?看下面:

首先,因为\(a_i\)两两互质,所以我们可以知道,\(\frac{(mul)}{a_i}\)一定不是\(a_i\)的倍数

而\(\frac{(mul)}{a_j}\)(j\(\ne\)i)一定是\({a_i}\)的倍数

所以,我们知道上式中除了第 \(i\) 项其他所有项 ≡ 0(mod\({a_i}\))

所以,原式 ≡ \(k_i\times\frac{(mul)}{a_i}\)(mod \(a_i\))

然后,我们又根据一开始那个式子的约束条件,得出\(k_i\times\frac{(mul)}{a_i}\)%\(a_i\)=\(b_i\)

所以,整个式子满足对\(a_i\)的约束条件

我们将这个推广开来,即可证得原式是crt方程组的一个解

然后根据前置知识三,即求得原始的最小解

你说了这么多,但该怎么实现呢?

我们设\(\frac{mul}{a_i}\)为\(m_i\)

那么,原式就变成了:

\(k_1 \times m_1 + k_2 \times m_2+......+k_n \times m_n\)

且\(k_i \times m_i\) ≡ \(b_i\) (mod \(a_i\))

** 这是什么?**

很多人表示一脸懵逼,不知道该怎么求???

好,让我们仔细看一下

我们由前置定理2可得:

如果\(k\times m_i\) ≡ 1 (mod \(a_i\))

那么当\(k_i=b_i \times k\)时

\(k_i \times m_i\) ≡ \(b_i\) (mod \(a_i\))

那么怎么求 \(k\) 呢?

我们知道有一个东西叫逆元。。

什么是逆元?

请出门左转以前的一篇日报

浅谈模质数意义下的乘法逆元

这里,我们就可以用逆元来解k咯

代码里我用的是exgcd哦

代码是重点:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define rii register int i
  6. #define rij register int j
  7. #define int long long
  8. using namespace std;
  9. int a[15],b[15],n;
  10. void exgcd(int a,int b,int &ls,int &x,int &y)//求逆元
  11. {
  12. if(b==0)
  13. {
  14. ls=a;
  15. x=1;
  16. y=0;
  17. }
  18. else
  19. {
  20. exgcd(b,a%b,ls,y,x);
  21. y-=(a/b)*x;
  22. }
  23. }
  24. signed main()
  25. {
  26. int mod=1,ls,y,x=0;
  27. scanf("%lld",&n);
  28. for(rii=1;i<=n;i++)
  29. {
  30. scanf("%lld%lld",&a[i],&b[i]);
  31. }
  32. for(rii=1;i<=n;i++)
  33. {
  34. mod*=a[i];
  35. }
  36. for(rii=1;i<=n;i++)
  37. {
  38. int kkk=mod/a[i];
  39. exgcd(a[i],kkk,ls,ls,y);
  40. x=(x+y*kkk*b[i])%mod;
  41. }
  42. int ans=(x+mod)%mod;
  43. printf("%lld",ans);
  44. }

毒瘤 Imagine(模板excrt出题人):我的\(a_i\)不互质!

好吧,我们进入下一步,

excrt

难的不是一点半点,好像和上一个已经没什么关系了

因为大家注意一点,上面理论的基础是\(a_i\)互质

那么这里这里不互质了我们怎么办呢?

(说暴力的站墙角)

但我们又没有好的解法......

看来,我们不得不学着暴力两两合并了

当然,我们不能像暴力一样通过自加两两合并

我们要用一个好玩的东西——\(exgcd\)

对于原方程的两个式子,我们可以化成这样(请跟着我的公式):

\[\begin{cases}x≡b_1 (mod a_1)\\x≡b_2 (mod a_2)\end{cases}
\]

则\(a_1 \times y+b_1=a_2 \times z+b_2\)

这就是一眼看穿的问题了

所以,这里就能用上面exgcd了

顺次两两合并

每次得出一个当前的解

合并很简单,

我们首先利用exgcd求出一个解

然后利用这个解,新构建一个新的方程带入计算

怎么构建新的解呢?

我们目前有两个方程,一个是由前面的方程合并来的,我们称它为方程1

另一个是我们这一步要合并的方程,称为方程2

\[\begin{cases}x≡b (mod a)---1\\x≡b_i (mod a_i)---2\end{cases}
\]

我们利用\(exgcd\)可以求出这里的x

现在的关键就在于如何合并\(a\)和\(a_i\)

我们知道,\(x\)+\(lcm\)(\(a\),\(a_i\))仍然满足上面的这个方程组

(因为\(lcm\)(\(a\),\(a_i\))%\(a\)=0,\(lcm\)(\(a\),\(a_i\))%\(a_i\)=0)

我们就可以得出一个新的方程了

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define rii register int i
  6. #define rij register int j
  7. #define int long long
  8. using namespace std;
  9. int a[100005],b[100005],n,cj,ans,mod;
  10. int exgcd(int a,int b,int &x,int &y)
  11. {
  12. if(b==0)
  13. {
  14. x=1;
  15. y=0;
  16. return a;
  17. }
  18. int gcd=exgcd(b,a%b,x,y);
  19. int zcq=x;
  20. x=y;
  21. y=zcq-a/b*y;
  22. return gcd;
  23. }
  24. int pw(int l,int r,int p)
  25. {
  26. int an=0;
  27. while(r>0)
  28. {
  29. if(r&1)
  30. {
  31. an=(an+l)%p;
  32. }
  33. l*=2;
  34. l%=p;
  35. r/=2;
  36. }
  37. return an;
  38. }
  39. signed main()
  40. {
  41. scanf("%lld",&n);
  42. for(rii=1;i<=n;i++)
  43. {
  44. scanf("%lld%lld",&a[i],&b[i]);
  45. }
  46. cj=a[1];
  47. ans=b[1];
  48. int x,y;
  49. for(rii=2;i<=n;i++)
  50. {
  51. int zcq=cj;
  52. int st=a[i];
  53. int ltt=(b[i]-ans%st+st)%st;
  54. int gys=exgcd(zcq,st,x,y);//求解
  55. int kkk=st/gys;
  56. x=pw(x,ltt/gys,kkk);
  57. ans+=cj*x;
  58. cj*=kkk;
  59. ans+=cj;
  60. ans%=cj;//防溢出(同时确保答案最小)
  61. }
  62. cout<<ans;
  63. }

会了后别忘了写NOI2018屠龙勇士哦~

特别感谢luogu曹冲养猪和excrt题解作者,他们给了我很多思路

如果出锅,请私信联系(评论我不一定看的到),万分感谢

浅析中国剩余定理(从CRT到EXCRT))的更多相关文章

  1. 欧几里得(辗转相除gcd)、扩欧(exgcd)、中国剩余定理(crt)、扩展中国剩余定理(excrt)简要介绍

    1.欧几里得算法(辗转相除法) 直接上gcd和lcm代码. int gcd(int x,int y){ ?x:gcd(y,x%y); } int lcm(int x,int y){ return x* ...

  2. 中国剩余定理(CRT)及其拓展(ExCRT)

    中国剩余定理 CRT 推导 给定\(n\)个同余方程 \[ \left\{ \begin{aligned} x &\equiv a_1 \pmod{m_1} \\ x &\equiv ...

  3. 中国剩余定理(crt)和扩展中国剩余定理(excrt)

    数论守门员二号 =.= 中国剩余定理: 1.一次同余方程组: 一次同余方程组是指形如x≡ai(mod mi) (i=1,2,…,k)的同余方程构成的组 中国剩余定理的主要用途是解一次同余方程组,其中m ...

  4. 学习笔记:中国剩余定理(CRT)

    引入 常想起在空间里见过的一些智力题,这个题你见过吗: 一堆苹果,\(3\)个\(3\)个地取剩\(1\)个,\(5\)个\(5\)个地取剩\(1\)个,\(7\)个\(7\)个地取剩\(2\)个,苹 ...

  5. 中国剩余定理(CRT)

    只看懂了CRT,EXCRT待补.... 心得:记不得这是第几次翻CRT了,每次都有迷迷糊糊的.. 中国剩余定理用来求解类似这样的方程组: 求解的过程中用到了同余方程. x=a1( mod x1) x= ...

  6. 中国剩余定理(CRT)与欧拉函数[数论]

    中国剩余定理 ——!x^n+y^n=z^n 想必大家都听过同余方程这种玩意,但是可能对于中国剩余定理有诸多不解,作为一个MOer&OIer,在此具体说明. 对于同余方程: x≡c1(mod m ...

  7. (light oj 1319) Monkey Tradition 中国剩余定理(CRT)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1319 In 'MonkeyLand', there is a traditional ...

  8. 扩展欧几里得(ex_gcd),中国剩余定理(CRT)讲解 有代码

    扩展欧几里得算法 求逆元就不说了. ax+by=c 这个怎么求,很好推. 设d=gcd(a,b) 满足d|c方程有解,否则无解. 扩展欧几里得求出来的解是 x是 ax+by=gcd(a,b)的解. 对 ...

  9. 清北学堂-DAY2-数论专题-中国剩余定理(CRT)

    首先请看定义:(百科上抄下来的)孙子定理是中国古代求解一次同余式组(见同余)的方法.是数论中一个重要定理.又称中国余数定理. 一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作&l ...

随机推荐

  1. python学习笔记之——正则表达式

    1.re模块 Python通过re模块提供对正则表达式的支持,re 模块使 Python 语言拥有全部的正则表达式功能.使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用 ...

  2. C++多线程编程(教程+Demo)

    下载地址:C++多线程编程(教程+Demo) Win32 SDK函数支持进行多线程的程序设计,并提供了操作系统原理中的各种同步.互斥和临界区等操作.Visual C++ 6.0中,使用MFC类库也实现 ...

  3. Creating dynamic/configurable parameterized queries in Entity Framework

    https://dillieodigital.wordpress.com/2013/05/09/creating-dynamicconfigurable-parameterized-queries-i ...

  4. vue知识day1

    HTML语义.CSS:样式 js:行为 jQuery:简化了js操作 boostrap :框架 ,以类方式展现 react:facebook 公司的产品 angular:谷歌公司产品 vue:作者尤雨 ...

  5. Android自定义View之绘制虚线

    现在实现一个效果,有个虚线分割和阴影效果.一个一个实现. 分为2中方式. 1.设计出图,我们SRC引入进来(最简单,但是需要其他资源支持). 2.code实现,有些难度,需要查资料. 现在把第2种方式 ...

  6. Mysql rpm安装

    总结下mysql rpm安装的方式,与一些错误 环境[root@host2 ~]# uname -aLinux host2 2.6.32-504.3.3.el6.x86_64 #1 SMP Wed D ...

  7. c#之反射(Reflection)

    一.反射是什么 反射就是.Net FrameWork框架为我们提供的一个帮助类库,它可以读取我们通过编译后生成的dll和exe文件里面metadate的信息. 反射可以动态的加载dll和exe文件,动 ...

  8. 7、ORM

    CRUD(create.retrieve.update.delete) left join right join inner join one2one one2many many2many 1.For ...

  9. Linux 系统网络问题处理集[包含VM处理]

    1.1. 新操作系统ping不同主机: 检查Linux服务器网段是否有etho的IP 查看/关闭防火墙 查看:service iptables status 关闭:service iptables s ...

  10. 安装office2016和激活。

    严重声明:条件宽裕的同学可以购买正版.请大家多多支持正版. 自己手贱,原本在电脑win10系统上安装的正版office被误删了,联系了客服人员,但是自己的微软账号也忘记了.好想下载个正版的.自己在网上 ...