中国剩余定理(CRT)

P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

对于线性同余方程组:

\[\begin{cases}
x\equiv a_1\pmod{b_1}\\
x\equiv a_2\pmod{b_2}\\
\dots\\
x\equiv a_n\pmod{b_n}\\
\end{cases}\]

其中\(b_i\)两两互质。

下文中,令\(N=\prod\limits_{i=1}^n b_i\)。


考虑如何求解其中一个特解\(x\)。

根据同余运算的可加性,我们可以将原方程拆成\(n\)个:

\[\begin{cases}
x_1\equiv a_1\pmod{b_1}\\
x_1\equiv 0\pmod{b_2}\\
\dots\\
x_1\equiv 0\pmod{b_n}\\
\end{cases}

\begin{cases}
x_2\equiv 0\pmod{b_1}\\
x_2\equiv a_2\pmod{b_2}\\
\dots\\
x_2\equiv 0\pmod{b_n}\\
\end{cases}

\dots

\begin{cases}
x_n\equiv 0\pmod{b_1}\\
x_n\equiv 0\pmod{b_2}\\
\dots\\
x_n\equiv a_n\pmod{b_n}\\
\end{cases}\]

则\(x=\sum_{i=1}^n x_i\)。

对于第一个方程组,我们令\(x_1=y_1\times a_1\),则有:

\[\begin{cases}
y_1\equiv 1\pmod{b_1}\\
y_1\equiv 0\pmod{b_2}\\
\dots\\
y_1\equiv 0\pmod{b_n}\\
\end{cases}\]

上面的转化过程是CRT的核心思想。

由后\(n-1\)个式子可知\(y_1\equiv 0\pmod{\text{lcm}_{i=2}^n\ b_i}\),即\(y_1\equiv 0\pmod{\frac{N}{b_1}}\),令\(c_i=\frac{N}{b_i}\),则\(y_1=c_1\times k_1(k_1\in \mathbb{Z})\)。

由第\(1\)个式子可知\(c_1\times k_1\equiv 1\pmod{b_1}\),于是\(k_1\equiv c_1^{-1}\pmod{b_1}\),即\(k_1\)是\(c_1\)在模\(b_1\)意义下的乘法逆元。

类比可得:

\[x_i=y_i\times a_i=c_i\times k_i\times a_i\\
x=\sum\limits_{i=1}^n c_i\times k_i\times a_i\]

至此完成\(x\)的求解。


再考虑如何用特解\(x\)表示所有的通解。

考虑\(x,y\)同为该方程组的解,则根据同余的传递性,有:

\[\begin{cases}
x\equiv y\pmod{b_1}\\
x\equiv y\pmod{b_2}\\
\dots\\
x\equiv y\pmod{b_n}
\end{cases}\]

也即\(x\equiv y\pmod{\text{lcm}_{i=1}^n b_i}\),即\(x\equiv y\pmod N\)。

所以通解可以表示为\(kN+x(k\in \mathbb{Z})\)。

这也告诉我们,对于\(k\in \mathbb{Z}\),该方程组的解在\([kN,(k+1)N)\)上是唯一的。


由于\(b_i\)不一定是质数,所以乘法逆元采用exGCD

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
int n,fac=1,a[N],b[N];
__int128 ans;
int exgcd(int a,int b,int& x,int& y){
int d;
if(b==0) x=1,y=0,d=a;
else d=exgcd(b,a%b,y,x),y-=a/b*x;
return d;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>b[i]>>a[i];
fac*=b[i];
}
for(int i=1;i<=n;i++){
int c=fac/b[i],k,t;
exgcd(c,b[i],k,t);
(ans+=(__int128)c*k*a[i])%=fac;
}
cout<<(long long)((ans+fac)%fac)<<"\n";
return 0;
}

扩展中国剩余定理(exCRT)

CRT不能解决\(b_i\)不两两互质的线性同余方程组。

这是因为模\(p\)意义下\(a\)的逆元存在\(\iff \gcd(a,p)=1\),即\(a,p\)互质。

而如果存在\(j\neq i\)使得\(\gcd(b_i,b_j)\neq 1\),则容易知道\(\gcd(b_i,c_i)\neq 1\),自然就不存在\(k_i\)。

这一缺陷来源于CRT的核心思想:构造\(x_i\)使得\(\begin{aligned}\begin{cases}
x_i\equiv 1\pmod{b_k} & k=i\\
x_i\equiv 0\pmod{b_k} & k\neq i
\end{cases}\end{aligned}\),这就注定了我们要使用乘法逆元。

exCRT则可以处理\(b_i\)不两两互质下的问题。

所谓exCRT,其实与CRT本身没有什么关系,它采用的是另一种思想——将同余式进行合并。

我们考虑\(2\)个同余式构成的方程组:

\[\begin{cases}
x\equiv a_1\pmod{b_1}\\
x\equiv a_2\pmod{b_2}
\end{cases}\]

要将其合并为\(x\equiv a\pmod b\)的形式。

我们将同余式重新表示为:\(x=a_1+k_1\times b_1=a_2+k_2\times b_2(k_1,k_2\in \mathbb{Z})\)。

变形可得:\(k_1\times b_1-k_2\times b_2=a_2-a_1\)。

这是一个典型的不定方程\(ax+by=c\)的形式,未知量是\((k_1,k_2)\),可以用exGCD求解。

如何解$ax+by=c$

具体来说,该方程有解\(\iff \gcd(a,b)\mid c\),必要性显然,充分性由裴蜀定理可得。

然后我们用exGCD求出\(ax+by=\gcd(a,b)\)的解,再将\(x,y\)同时乘\(\frac{c}{\gcd(a,b)}\)即可。


若存在\(ax+by=c\),则可以根据特解\(x,y\)求出任意通解\(x',y'\):

\(\begin{cases}
x'=x+k\times \frac{b}{\gcd(a,b)}\\
y'=y-k\times \frac{a}{\gcd(a,b)}
\end{cases}(k\in \mathbb{Z})\)

若解得特解为\((k_1',k_2')\),通解\((k_1,k_2)\)即为\((k_1'+k\times\frac{b_2}{\gcd(b_1,b_2)},k_2'+k\times\frac{b_1}{\gcd(b_1,b_2)})\),其中\(k\in \mathbb{Z}\)。

选择其一带入:

\[\begin{aligned}
x&=a_1+k_1\times b_1\\
&=a_1+(k_1'+k\times\frac{b_2}{\gcd(b_1,b_2)})\times b_1\\
&=a_1+k_1'\times b_1+k\times\text{lcm}(b_1,b_2)
\end{aligned}\]

即:

\[x\equiv a_1+k_1'\times b_1\pmod{\text{lcm}(b_1,b_2)}
\]

然后重复上述步骤,直到所有式子合并为\(1\)个为止。

点击查看代码
#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int N=1e5+10;
long long tmp;
int n,a[N],b[N];
int exgcd(int a,int b,int& x,int& y){
int d;
if(b==0) x=1,y=0,d=a;
else d=exgcd(b,a%b,y,x),y-=a/b*x;
return d;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>tmp,n=tmp;
for(int i=1;i<=n;i++) cin>>tmp,b[i]=tmp,cin>>tmp,a[i]=tmp;
int pb=b[1],pa=a[1],k1,k2;
for(int i=2;i<=n;i++){
int g=exgcd(pb,b[i],k1,k2);//实际上应该是-b[i],为了避免负数所以这样写,注意此时k2要取相反数
//if((a[i]-pa)%g) cout<<"no solution\n";//保证有解
k1=(a[i]-pa)/g*k1;
pa=pa+pb*k1;
pb=pb/g*b[i];
pa%=pb;
}
tmp=(pa+pb)%pb;
cout<<tmp<<"\n";
return 0;
}

(实际上,也有在CRT的基础上动“小手术”来解决exCRT的方法,见此文,我没有学就不放了)

例题

会不断添加。

P3868 [TJOI2009] 猜数字

变形一下:

\[b_i\mid (n-a_i)\\
n-a_i\equiv 0\pmod{b_i}\\
n\equiv a_i\pmod{b_i}\]

然后是CRT的板子了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
int n,fac=1,a[N],b[N];
__int128 ans;
int exgcd(int a,int b,int& x,int& y){
int d;
if(b==0) x=1,y=0,d=a;
else d=exgcd(b,a%b,y,x),y-=a/b*x;
return d;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i],fac*=b[i];
for(int i=1;i<=n;i++){
int c=fac/b[i],k,t;
exgcd(c,b[i],k,t);
(ans+=(__int128)c*k*a[i])%=fac;
}
cout<<(long long)((ans+fac)%fac)<<"\n";
return 0;
}

P4774 [NOI2018] 屠龙勇士

我们发现,用于对付每条龙的剑是固定的,因此我们预处理出对付第\(i\)条龙的剑的攻击力,记为\(b_i\)。

第\(i\)条龙能被击杀,当且仅当\(p_i\mid a_i-b_ix\),也即\(b_ix\equiv a_i\pmod{p_i}\)。

所以我们实际上是要求解下面的线性同余方程组:

\[\begin{cases}
b_1x\equiv a_1\pmod{p_1}\\
b_2x\equiv a_2\pmod{p_2}\\
\dots\\
b_nx\equiv a_n\pmod{p_n}\\
\end{cases}\]

我们发现,相比之前的方程,此题在\(x\)前面加了一个系数\(b_i\)。

由于\(p\)不互质,所以采用exCRT。

首先求\(b_i\)在模\(p_i\)意义下的逆元是不可行的,因为\(b_i,p_i\)未必互质。

让我们看看能否沿用之前的思路来分析。

合并成带参的形式

仍然取两个方程,试图将它们合并。

\[\begin{cases}
b_1x\equiv a_1\pmod{p_1}\\
b_2x\equiv a_2\pmod{p_2}
\end{cases}\]
\[\begin{cases}
b_1x=a_1+p_1k_1\\
b_2x=a_2+p_2k_2
\end{cases}(k_1,k_2\in \mathbb{Z})\]

发现\(x\)前系数不同,无法直接合并,因此不妨让系数变成\(\text{lcm}(b_1,b_2)=L\)。

\[\begin{cases}
Lx=\frac{L}{b_1}(a_1+p_1k_1)\\
Lx=\frac{L}{b_2}(a_2+p_2k_2)
\end{cases}\]

统一系数后,就和exCRT基本相同了,可以类比上面的分析。

将两式联立并整理,得到:

\[\frac{L}{b_1}(a_1+p_1k_1)=\frac{L}{b_2}(a_2+p_2k_2)\\
k_1\frac{L}{b_1}p_1-k_2\frac{L}{b_2}p_2=\frac{L}{b_2}a_2-\frac{L}{b_1}a_1\]

解线性方程组得到特解\((k_1',k_2')\),根据上面的结论,通解\((k_1,k_2)\)可以表示为\((k\in \mathbb{Z})\):

\[\large(k_1'+k\frac{\frac{L}{b_2}p_2}{\gcd(\frac{L}{b_1}p_1,\frac{L}{b_2}p_2)},k_2'+k\frac{\frac{L}{b_1}p_1}{\gcd(\frac{L}{b_1}p_1,\frac{L}{b_2}p_2)})
\]

任选其一带入:

\[\begin{aligned}
\large Lx&=\frac{L}{b_1}(a_1+p_1k_1)\\
&=\dots\\
&=\frac{L}{b_1}(a_1+p_1k_1)+k\times \text{lcm}(\frac{L}{b_1}p_1,\frac{L}{b_2}p_2)
\end{aligned}\]

变成同余式的形式:

\[Lx\equiv \frac{L}{b_1}(a_1+p_1k_1) \pmod{\text{lcm}(\frac{L}{b_1}p_1,\frac{L}{b_2}p_2)}
\]

至此完成合并。

最终得到的是一个线性同余方程\(bx\equiv a\pmod p\),理论上应该可以化成不定方程用exGCD来解。不过代码到现在还没写出来,有一些奇怪的问题(^^;

合并成不带参的形式

我们当然更希望最终合并成的同余方程式中,\(x\)系数是\(1\)。

因此假设之前的方程式已经合并成了该形式,现在向其中添加一个带系数的同余方程式:

\[\begin{aligned}\begin{cases}
x&\equiv a\pmod p\\
b_ix&\equiv a_i\pmod{p_i}
\end{cases}\end{aligned}\]
\[\begin{aligned}\begin{cases}
x&=a+k_1p&(1)\\
b_ix&=a_i+k_2p_i&(2)
\end{cases}\end{aligned}\]

将\((1)\)式乘\(b_i\),两式联立并整理,得到:

\[k_1b_ip-k_2p_i=a_i-ab_i
\]

exGCD解不定方程得到\((k_1',k_2')\),通解\((k_1,k_2)\)表示为\((k\in\mathbb{Z})\):

\[(k_1'+k\frac{p_i}{\gcd(b_ip,p_i)},k_2+k\frac{b_ip}{\gcd(b_ip,p_i)})
\]

由于我们想要\(x\)的系数为\(1\),所以选择\(k_1\)带入\((1)\)式:

\[x=a+k_1p+k\frac{p_ip}{\gcd(b_ip,p_i)}
\]

变成同余式的形式:

\[x\equiv a+k_1p\pmod{\frac{p_ip}{\gcd(b_ip,p_i)}}
\]

至此完成合并。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int t,n,m,a[N],p[N],z[N],b[N];
multiset<int> se;
int exgcd(int a,int b,int& x,int& y){
int d;
if(b==0) x=1,y=0,d=a;
else d=exgcd(b,a%b,y,x),y-=a/b*x;
return d;
}
int solve(){
se.clear();
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>p[i];
for(int i=1;i<=n;i++) cin>>z[i];
for(int i=1,x;i<=m;i++) cin>>x,se.insert(x);
int pp=1,pa=0,C,g,k1,k2,mx=0,tmp;
for(int i=1;i<=n;i++){
auto it=se.upper_bound(a[i]);
if(it!=se.begin()) --it;
b[i]=*it,se.erase(it),se.insert(z[i]);
mx=max(mx,(a[i]-1)/b[i]+1);
}
for(int i=1;i<=n;i++){
g=exgcd((__int128)b[i]*pp%p[i],p[i],k1,k2);
C=(a[i]-b[i]*pa)%p[i];
if(C%g) return -1;
pa+=(__int128)k1*(C/g)%(p[i]/g)*pp%(tmp=p[i]/g*pp);
pa%=(pp=tmp);
}
if(pa<mx) pa+=((mx-pa-1)/pp+1)*pp;
return pa;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
while(t--) cout<<solve()<<"\n";
return 0;
}

\[\mathbb{\mid TO\ BE\ CONTINUED\mid}
\]

[笔记]中国剩余定理(CRT) & 扩展中国剩余定理(exCRT)的更多相关文章

  1. 中国剩余定理(CRT) & 扩展中国剩余定理(ExCRT)总结

    中国剩余定理(CRT) & 扩展中国剩余定理(ExCRT)总结 标签:数学方法--数论 阅读体验:https://zybuluo.com/Junlier/note/1300035 前置浅讲 前 ...

  2. 扩展中国剩余定理 (ExCRT)

    扩展中国剩余定理 (ExCRT) 学习笔记 预姿势: 扩展中国剩余定理和中国剩余定理半毛钱关系都没有 问题: 求解线性同余方程组: \[ f(n)=\begin{cases} x\equiv a_1\ ...

  3. 算法学习笔记(9): 中国剩余定理(CRT)以及其扩展(EXCRT)

    扩展中国剩余定理 讲解扩展之前,我们先叙述一下普通的中国剩余定理 中国剩余定理 中国剩余定理通过一种非常精巧的构造求出了一个可行解 但是毕竟是构造,所以相对较复杂 \[\begin{cases} x ...

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

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

  5. 欧几里得(辗转相除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* ...

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

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

  7. 中国剩余定理(CRT)及其扩展(EXCRT)详解

    问题背景   孙子定理是中国古代求解一次同余式方程组的方法.是数论中一个重要定理.又称中国余数定理.一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作<孙子算经>卷下第 ...

  8. 扩展中国剩余定理(EXCRT)学习笔记

    扩展中国剩余定理(EXCRT)学习笔记 用途 求解同余方程组 \(\begin{cases}x\equiv c_{1}\left( mod\ m_{1}\right) \\ x\equiv c_{2} ...

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

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

  10. P4777 【模板】扩展中国剩余定理(EXCRT)/ poj2891 Strange Way to Express Integers

    P4777 [模板]扩展中国剩余定理(EXCRT) excrt模板 我们知道,crt无法处理模数不两两互质的情况 然鹅excrt可以 设当前解到第 i 个方程 设$M=\prod_{j=1}^{i-1 ...

随机推荐

  1. 6款超好用的AI换脸软件,一键视频直播换脸(附下载链接)

    随着AIGC的火爆,AI换脸技术也被广泛应用于娱乐.广告.电影制作等领域,本期文章系统介绍了市面上超火的6款AI软件 换脸整合包收录了全部6款AI工具,请按照需要选择下载: 百度网盘:https:// ...

  2. 文件服务器nginx-file-browser

    说明 我们前面介绍过File Browser这款文件服务器的部署和使用,在部署和配置时可以控制权限和分享等功能,这里我们介绍一款更简单而且主题特别好看的文件服务器,不涉及权限的控制,直接可以对外分享. ...

  3. ChatMoney智能知识库让你轻松工作!

    本文由 ChatMoney团队出品 为了增强企业内部知识的传递和共享效率,最近花了两周时间测试Chatmoney知识库 +企微客服助手模式,测试效果让我很惊喜! 对话引用知识库内容,Chatmoney ...

  4. Eplan是什么软件?学习Eplan软件的几个关键要点

    EPLAN是一款电气计算机辅助设计软件.我是一名Eplan软件的学习者,最近在学习这个专业的电气设计软件时,总结了一些关键要点,希望能与大家分享. 1. 熟悉软件界面和功能:首先,我们需要熟悉Epla ...

  5. Ubuntu云服务器上部署发布Vite项目

    1 拷贝代码 一般来说是Windows环境下开发,Ubuntu环境下部署.因此首先要考虑的问题是如何将Vite项目的源代码拷贝到云服务器上面去.最简单的就是使用像MobaXterm这样的远程连接工具, ...

  6. 从Rust想到C#

    近几年,RUST语言越来越受大家的喜爱,排除去一些跟风者,大部分的人喜欢RUST的内存安全性和高效的性能.但编译速度始终是它的短板. 这几天,突然有一个想法,如果C#或者说.NET的编译器也做成RUS ...

  7. rollup开发一个npm插件/包

    创建一个项目 创建一个空项目,并初始化npm init -y 局部安装rollup yarn add --dev rollup 并创建其配置文件 rollup.config.js export def ...

  8. CentOS7安装SMPlayer和VLC Player

    原文地址 我装完之后,看大概是1个多G的MP4视频依然是觉得有点卡.不知道为什么,流畅度照比Windows下的视频播放器差多了. You will need to also install the E ...

  9. 【免费】AList替代开源项目—Openlist

    可能大家之前关注过alist,我之前的文章有过响应的介绍:AList搭建网盘挂载硬盘并挂载网络资源 – 心一信息 Alist教程第二期 – 心一信息 其实,Alist是一个开源项目,它可以将 40 多 ...

  10. 通过Web ETL统一调度和管理DataX任务

    DataX是一款功能强大的数据集成平台,但是其无WEB管理界面使得DataX任务在管理与调度方面存在不少问题,任务多了后很难管理和维护,同时使用和学习成本也比较高.今天就介绍下用RestCloud E ...