\(crt,Chinese\ Remainder\ Theorem\)

概述

  • 前置技能:同余基础性质,\(exgcd\).
  • \(crt\),中国剩余定理.用于解决模数互质的线性同余方程组.大概长这样:

\[\begin{equation}
\left\{
\begin{array}{lr}
x\equiv a_1(mod\ m_1),\\
x\equiv a_2(mod\ m_2),\\
x\equiv a_3(mod\ m_3),\\
......\\
x\equiv a_n(mod\ m_n).\\
\end{array}
\right.
\end{equation}
\]

  • 其中,所有的 \(m\) 两两互质,否则需要使用 \(excrt\),此处不讨论.
  • 记\(N=\prod m_i\),对于每个方程\(x\equiv a_i(mod\ m_i)\),尝试找一个 \(y_i\) 满足\((N/m_i)*y_i\equiv 1(mod\ m_i).\)
  • 这里的\(N/m_i\)就是其他所有模数的积,因为所有 \(m\) 互质,所以\(N/m_i\)与\(m_i\)互质.那么上面的线性同余方程一定可以通过 \(exgcd\) 找到这样的 \(y_i\).
  • 再记\(x_i=(N/m_i)*y_i\),那么\(x_i*a_i\equiv a_i(mod\ m_i),x_i*a_i\equiv 0(mod\ m_j,\forall j\not= i)\).
  • 将各个方程合并起来即可得出原方程组的解\(x=\sum x_ia_i\),容易验证,这个构造出的 \(x\) 满足原方程组,且对于\(\forall x'\equiv x(mod\ N),x'\)也是合法的解.
  • 时间复杂度为\(O(n\cdot(log\prod m_i))\).

应用

  • 解线性同余方程组,拆分处理,合并答案.

题目

曹冲养猪

  • 题意:求解一个线性同余方程组的最小正整数解,其中模数互质.
  • 直接使用 \(crt\) 求解.因为要求最小正整数解,所以在合并的时候对 \(N\) 取模,且最后确保为正.
  1. #include"bits/stdc++.h"
  2. #define int long long
  3. using namespace std;
  4. typedef long long LoveLive;
  5. inline int read()
  6. {
  7. int out=0,fh=1;
  8. char jp=getchar();
  9. while ((jp>'9'||jp<'0')&&jp!='-')
  10. jp=getchar();
  11. if (jp=='-')
  12. {
  13. fh=-1;
  14. jp=getchar();
  15. }
  16. while (jp>='0'&&jp<='9')
  17. {
  18. out=out*10+jp-'0';
  19. jp=getchar();
  20. }
  21. return out*fh;
  22. }
  23. const int MAXN=11;
  24. int a[MAXN],m[MAXN];
  25. int n;
  26. void exgcd(int a,int b,int &x,int &y)
  27. {
  28. if(!b)
  29. {
  30. x=1;
  31. y=0;
  32. return;
  33. }
  34. exgcd(b,a%b,x,y);
  35. int t=x;
  36. x=y;
  37. y=t-(a/b)*y;
  38. }
  39. int crt(int a[],int m[],int n)
  40. {
  41. int res=0,N=1,x,y;
  42. for(int i=1;i<=n;++i)
  43. N*=m[i];
  44. for(int i=1;i<=n;++i)
  45. {
  46. int tmp=N/m[i];
  47. exgcd(m[i],tmp,x,y);
  48. res=(res+y*tmp*a[i])%N;
  49. }
  50. return (res+N)%N;
  51. }
  52. signed main()
  53. {
  54. n=read();
  55. for(int i=1;i<=n;++i)
  56. m[i]=read(),a[i]=read();
  57. int ans=crt(a,m,n);
  58. printf("%lld\n",ans);
  59. return 0;
  60. }

\(Excrt,Extended\ crt\)

概述

  • 前置技能:\(crt\)(其实不是很必要),\(exgcd\).
  • 在 \(crt\) 中,要求所有模数互质.如果模数不互质, \(crt\) 就解决不了问题.因为构造过程中会出现\(N/m_i\)与\(m_i\)不互质的情况,同余方程\((N/m_i)*y_i\equiv 1(mod\ m_i)\)会出现无解的情况.
  • 这时候就需要使用 \(excrt\) 进行处理.
  • \(excrt\) 的基本思路与 \(crt\) 不同,\(crt\) 是直接通过构造得出解, \(excrt\) 是通过将方程两两合并,直到最后只剩下一个方程,也就得到了通解.
  • 对于如下方程组:

\[\begin{equation}\left\{ \begin{array}{lr} x\equiv a_1(mod\ m_1),\\ x\equiv a_2(mod\ m_2),\\ x\equiv a_3(mod\ m_3),\\ ......\\ x\equiv a_n(mod\ m_n).\\ \end{array}\right.\end{equation}
\]

  • 先考虑合并前两个方程,将其写成一个等价的线性同余方程.
  • 由同余的基本性质可知,一个合法解 \(x_0\) 满足 \(x_0=a_1+k_1*m_1=a_2+k_2*m_2.\)
  • 将后两个式子移项整理可得\(k_2*m_2-k_1*m_1=a_1-a_2.\)
  • 这里就是线性不定方程的形式了,尝试使用 \(exgcd\) 求解.
  • 可记\(gcd(m_1,m_2)=g,a_1-a_2=c.\)那么当\(g\not|\ c\)时,显然无解,这两个方程无法同时满足,那么整个方程组也就无解.
  • 否则,先通过 \(exgcd\) 求出满足方程 \(k_2*m_2-k_1*m_1=g\)的一组解\(k_2',-k_1'\).
  • 方程两边乘上\(c/g\),即\(-k_1'\)乘上\(c/g\)就得到了\(-k_1\).这里可以让\(-k_1\)对\(m_2\)取模,防止其过大.
  • 然后代入第一个方程求出\(x_0=a_1+k_1*m_1\),注意上一步求出的是\(-k_1\),这里要取负号.
  • 这两个方程的通解即为\(x=x_0+lcm(m_1,m_2)\),写成同余式为\(x\equiv x_0(mod\ lcm(m_1,m_2)).\)
  • 新得到的同余式和原来的两个同余式并在一起是等价的,那么将新得到的同余式继续按照上述步骤与其他同余式合并,直到最后只剩下一个同余式,就是要求的通解了.
  • 时间复杂度为\(O(n\cdot log(\prod m_i)).\)

应用

  • 解线性同余方程组,合并答案.

题目

Strange Way to Express Integers

  • 题意:求解一个线性同余方程组的最小正整数解,不保证模数互质.
  • 使用 \(excrt\) 处理即可.注意调整解.
  1. #include"bits/stdc++.h"
  2. #define int long long
  3. using namespace std;
  4. typedef long long LoveLive;
  5. inline int read()
  6. {
  7. int out=0,fh=1;
  8. char jp=getchar();
  9. while ((jp>'9'||jp<'0')&&jp!='-')
  10. jp=getchar();
  11. if (jp=='-')
  12. {
  13. fh=-1;
  14. jp=getchar();
  15. }
  16. while (jp>='0'&&jp<='9')
  17. {
  18. out=out*10+jp-'0';
  19. jp=getchar();
  20. }
  21. return out*fh;
  22. }
  23. const int MAXN=1e5+10;
  24. int a[MAXN],m[MAXN],n;
  25. int exgcd(int a,int b,int &x,int &y)
  26. {
  27. if(!b)
  28. {
  29. x=1;
  30. y=0;
  31. return a;
  32. }
  33. int d=exgcd(b,a%b,x,y);
  34. int t=x;
  35. x=y;
  36. y=t-(a/b)*y;
  37. return d;
  38. }
  39. int excrt(int a[],int m[],int n)
  40. {
  41. int k1,k2;
  42. for(int i=2;i<=n;++i)
  43. {
  44. int g=exgcd(m[i],m[1],k2,k1);
  45. int c=a[1]-a[i];
  46. if(c%g)
  47. return 0;
  48. k1*=c/g;
  49. k1%=m[i];
  50. a[1]=a[1]+(-k1)*m[1];//注意修改的先后顺序
  51. m[1]=m[1]*m[i]/g;
  52. }
  53. return 1;
  54. }
  55. signed main()
  56. {
  57. while(~scanf("%d",&n))
  58. {
  59. for(int i=1;i<=n;++i)
  60. m[i]=read(),a[i]=read();
  61. int flag=excrt(a,m,n);
  62. if(!flag)
  63. puts("-1");
  64. else
  65. printf("%lld\n",(a[1]%m[1]+m[1])%m[1]);
  66. }
  67. return 0;
  68. }

屠龙勇士

  • 题面比较长...直接看链接内容即可(注意数据表格).
  • 每轮剑的攻击力可以用一个 \(multiset\) 维护,可以直接预处理出来,记每轮攻击的攻击力为\(atk_i\).
  • 考虑记要求的攻击次数为 \(x\) ,则每次攻击为使恢复若干次后生命值恰好为 \(0\) ,只需满足\(atk_i*x\geq a_i,atk_i*x \equiv a_i(mod\ p_i).\)
  • 注意到数据中要么满足特性 \(1\) , 即\(a_i\leq p_i\),要么满足所有的 \(p_i=1\).
  • 那么可以判断后分情况处理,对于满足特性 \(1\) 的部分,显然只需处理同余式,同余式成立的情况下一定有\(atk_i*x\geq a_i\).对于所有 \(p_i=1\) 的部分,只需求出将每只龙的生命值打到非正数的次数,取最大值即可,即\(\max \lceil a_i/atk_i \rceil\),容易处理.
  • 对于 \(a_i\leq p_i\) 的部分,我们得到若干个同余式,考虑使用 \(excrt\) 进行处理.
  • 同余式形式是 \(atk_i*x \equiv a_i(mod\ p_i)\) ,需要先转化为 \(excrt\) 的标准形式,即

\[\begin{equation}\left\{ \begin{array}{lr} x\equiv a_1(mod\ m_1),\\ x\equiv a_2(mod\ m_2),\\ x\equiv a_3(mod\ m_3),\\ ......\\ x\equiv a_n(mod\ m_n).\\ \end{array}\right.\end{equation}
\]

  • 考虑将同余式两边乘上逆元 \(atk_i^{-1}\),可得到\(x \equiv atk_i^{-1}*a_i(mod\ p_i)\),就化为了标准形式.
  • 但 \(atk_i\) 与 \(p_i\) 可能不互质,此时 \(atk_i\) 是没有逆元的.可以根据同余的消去律,将\(atk_i,a_i,p_i\)都约去三者的 \(gcd\) .若此时 \(atk_i,p_i\) 仍不互质,则整个方程组也就无解了.否则求出逆元后,转化为标准形式.最后对所有转化后的同余式使用 \(excrt\) 求解.
  • 注意中间运算会出现$long\ long\ *\ long\ long \ % \ long\ long $的情况,使用快速乘,即将快速幂中的乘法运算改为加法运算,中途取模.
  1. #include"bits/stdc++.h"
  2. #define int long long
  3. using namespace std;
  4. typedef long long LoveLive;
  5. inline int read()
  6. {
  7. int out=0,fh=1;
  8. char jp=getchar();
  9. while ((jp>'9'||jp<'0')&&jp!='-')
  10. jp=getchar();
  11. if (jp=='-')
  12. {
  13. fh=-1;
  14. jp=getchar();
  15. }
  16. while (jp>='0'&&jp<='9')
  17. {
  18. out=out*10+jp-'0';
  19. jp=getchar();
  20. }
  21. return out*fh;
  22. }
  23. multiset<int> sword;
  24. multiset<int>::iterator it;
  25. int n,m;
  26. const int MAXN=1e5+10;
  27. int fmul(int a,int b,int mod)
  28. {
  29. assert(mod>0);
  30. a=(a%mod+mod)%mod;
  31. b=(b%mod+mod)%mod;
  32. int res=0;
  33. while(b)
  34. {
  35. if(b&1)
  36. res=(res+a)%mod;
  37. a=(a+a)%mod;
  38. b>>=1;
  39. }
  40. return res;
  41. }
  42. int exgcd(int a,int b,int &x,int &y)
  43. {
  44. if(!b)
  45. {
  46. x=1;
  47. y=0;
  48. return a;
  49. }
  50. int d=exgcd(b,a%b,x,y);
  51. int t=x;
  52. x=y;
  53. y=t-(a/b)*y;
  54. return d;
  55. }
  56. int a[MAXN],p[MAXN],atk[MAXN];
  57. int award[MAXN];
  58. bool check()
  59. {
  60. for(int i=1;i<=n;++i)
  61. if(p[i]!=1)
  62. return false;
  63. return true;
  64. }
  65. void pre()
  66. {
  67. for(int i=1;i<=n;++i)
  68. {
  69. it=sword.upper_bound(a[i]);
  70. if(it!=sword.begin())
  71. --it;
  72. atk[i]=*it;
  73. sword.erase(it);
  74. sword.insert(award[i]);
  75. }
  76. }
  77. void solve_sp()
  78. {
  79. int ans=-1;
  80. for(int i=1;i<=n;++i)
  81. {
  82. ans=max(ans,(a[i]+atk[i]-1)/atk[i]);
  83. }
  84. printf("%lld\n",ans);
  85. }
  86. int A[MAXN],M[MAXN];
  87. int inv(int k,int mod)
  88. {
  89. int x,y;
  90. exgcd(k,mod,x,y);
  91. return (x%mod+mod)%mod;
  92. }
  93. bool build_equations()
  94. {
  95. int x,y;
  96. for(int i=1;i<=n;++i)
  97. {
  98. int g=exgcd(a[i],p[i],x,y);
  99. g=exgcd(g,atk[i],x,y);
  100. atk[i]/=g;
  101. a[i]/=g;
  102. p[i]/=g;
  103. int flag=exgcd(atk[i],p[i],x,y);
  104. if(flag!=1)
  105. return false;
  106. A[i]=fmul(a[i],inv(atk[i],p[i]),p[i]);
  107. M[i]=p[i];
  108. }
  109. return true;
  110. }
  111. void excrt()
  112. {
  113. int k1,k2;
  114. for(int i=2;i<=n;++i)
  115. {
  116. int g=exgcd(M[i],M[1],k2,k1);
  117. int c=A[1]-A[i];
  118. if(c%g)
  119. {
  120. puts("-1");
  121. return;
  122. }
  123. k1=fmul(k1,c/g,M[i]);
  124. /*k1*=c/g;
  125. k1%=M[i];*/
  126. A[1]=A[1]-fmul(k1,M[1],M[1]*(M[i]/g));//打括号,防止爆long long
  127. M[1]=M[1]*(M[i]/g);
  128. A[1]%=M[1];
  129. }
  130. printf("%lld\n",(A[1]%M[1]+M[1])%M[1]);
  131. }
  132. signed main()
  133. {
  134. // freopen("dragon8.in","r",stdin);
  135. int T=read();
  136. while(T--)
  137. {
  138. sword.clear();
  139. n=read();
  140. m=read();
  141. for(int i=1;i<=n;++i)
  142. a[i]=read();
  143. for(int i=1;i<=n;++i)
  144. p[i]=read();
  145. for(int i=1;i<=n;++i)
  146. award[i]=read();
  147. for(int i=1;i<=m;++i)
  148. sword.insert(read());
  149. pre();
  150. if(check())
  151. {
  152. solve_sp();
  153. continue;
  154. }
  155. if(build_equations())
  156. excrt();
  157. else
  158. puts("-1");
  159. }
  160. return 0;
  161. }

crt,excrt学习总结的更多相关文章

  1. CRT&EXCRT学习笔记

    非扩展 用于求解线性同余方程组 ,其中模数两两互质 . 先来看一看两个显然的定理: 1.若 x \(\equiv\) 0 (mod p) 且 y \(\equiv\) 0 (mod p) ,则有 x+ ...

  2. CRT & EXCRT 学习笔记

    这玩意解决的是把同余方程组合并的问题. CRT的核心思想和拉格朗日插值差不多,就是构造一组\(R_i\)使得$\forall i,j(i \neq j) $ \[R_im_i = 1, R_im_j ...

  3. 扩展中国剩余定理 exCRT 学习笔记

    前言 由于 \(\{\mathrm{CRT}\}\subseteq\{\mathrm{exCRT}\}\),而且 CRT 又太抽象了,所以直接学 exCRT 了. 摘自 huyufeifei 博客 这 ...

  4. [笔记] CRT & exCRT

    [笔记] CRT & exCRT 构造法 求多组\(x \equiv r_i (\bmod d_i)\)的解,\(d_i\)互质 余数\((r_i = remainder)\),除数\((d_ ...

  5. CRT和EXCRT学习笔记

    蒟蒻maomao终于学会\(CRT\)啦!发一篇博客纪念一下(还有防止忘掉) \(CRT\)要解决的是这样一个问题: \[x≡a_1​(mod m_1​)\] \[x≡a_2​(mod m_2​)\] ...

  6. CRT&EXCRT 中国剩余定理及其扩展

    前言: 中国剩余定理又名孙子定理.因孙子二字歧义,常以段子形式广泛流传. 中国剩余定理并不是很好理解,我也理解了很多次. CRT 中国剩余定理 中国剩余定理,就是一个解同余方程组的算法. 求满足n个条 ...

  7. CRT && exCRT模板

    CRT从各种方面上都吊打exCRT啊...... 短,好理解... 考虑构造bi使得bi % pi = ai,bi % pj = 0.然后全加起来就行了. 显然bi的构造就是ai * (P/pi) * ...

  8. [note]CRT&exCRT

    中国剩余定理 别人的blog 假设现在有关于x的同余方程组(p1,p2均为质数) \(x=a_1\pmod {p_1}\) \(x=a_2\pmod {p_2}\) 可以转化成如下形式 \(x=a_1 ...

  9. 「中国剩余定理CRT」学习笔记

    设正整数$m_1, m_2, ... , m_r$两两互素,对于同余方程组 $x ≡ a_1 \ (mod \ m_1)$ $x ≡ a_2 \ (mod \ m_2)$ $...$ $x ≡ a_r ...

随机推荐

  1. Python基础笔记系列十三:socket网络编程

    本系列教程供个人学习笔记使用,如果您要浏览可能需要其它编程语言基础(如C语言),why?因为我写得烂啊,只有我自己看得懂!!使用python编写一个简易的服务端程序和客户端程序,启动服务端和客户端(监 ...

  2. 微信公众平台开发(一) 配置接口与验证URL

    一.简介 微信公众平台是腾讯公司在微信的基础上新增的功能模块,通过这一平台,个人和企业都可以打造一个微信的公众号,并实现和特定群体的文字.图片.语音的全方位沟通.互动. 二.通讯机制 三.注册微信平台 ...

  3. 关于 MongoDB 复制集

    为什么要使用复制集 1.备份数据通过自带的 mongo_dump/mongo_restore 工具也可以实现备份,但是毕竟没有复制集的自动同步备份方便. 2.故障自动转移部署了复制集,当主节点挂了后, ...

  4. pandas的常用函数

    1.DataFrame的常用函数: (1)np.abs(frame) 绝对值, (2)apply function, lambda f= lambda x: x.max()-x.min(),frame ...

  5. mvc 获取 HtmlHelper 表达式值

    public static MvcHtmlString Try<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, ...

  6. 代码题 — 剑指offer题目、总结

    剑指offer题目总结:  https://www.cnblogs.com/dingxiaoqiang/category/1117681.html 版权归作者所有,任何形式转载请联系作者.作者:马孔多 ...

  7. [洛谷U62358]求导函数

    U62358 求导函数 题面 给出一个n次函数\(f(x)=a_{n}x^{n}+a_{n-1}x^{n-1}+...+a_{1}x+a_0\)的各项系数\(a_n,a_{n-1}...a_1,a_0 ...

  8. 什么情况下用resultType和 resultMap

    MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap 那什么情况下用resultType? resultMap 一般用在什么情况下?   如 ...

  9. UVA-1312 Cricket Field (技巧枚举)

    题目大意:在一个w*h的网格中,有n个点,找出一个最大的正方形,使得正方形内部没有点. 题目分析:寻找正方形实质上等同于寻找矩形(只需令长宽同取较短的边长).那么枚举出所有可能的长宽组合取最优答案即可 ...

  10. POJ 3167 Cow Pattern ★(KMP好题)

    题意 给你一个数字序列S,再给一个数字序列pattern,S和pattern中的数字都是1到s(s<=25).每个序列里的数字都有个排名,也就是第几小,现在我们要用pattern来匹配S.在本题 ...