中国剩余定理(CRT)
CRT
中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组
\[\begin{cases} x \equiv r_1\ ({\rm mod}\ p_1) \\ x\equiv r_2\ ({\rm mod}\ p_2) \\ ... \\ x \equiv r_n\ ({\rm mod}\ p_n)\end{cases}
\]
其中 \(p_1, p_2 ... p_n\)两两互质
结论
1 . 计算所有模数的积 \(n\) ;
2 . 对于第 \(i\) 个方程:
- 计算 \(m_i=\frac{n}{n_i}\) ;
- 计算 \(m_i\) 在模 \(n_i\) 意义下的逆元 \(m_i^{-1}\) ;
- 计算 \(c_i=m_i \cdot m_i^{-1}\)(不要对 \(n_i\) 取模)。
3 . 方程组在模 n 意义下的唯一解为:\(x=\sum_{i=1}^k a_i \cdot c_i \pmod n\) 。
il int crt(int a[],int n[],int k){
int x=0,N=1;
for(ri int i=1;i<=k;++i) N*=n[i];
for(ri int i=1,m,inv,y;i<=k;++i){
m=N/n[i];
exgcd(m,n[i],inv,y);
inv=(inv%n[i]+n[i])%n[i];
x=(x+a[i]*m*inv%N)%N;
}
return (x%N+N)%N;
}
code
#include<bits/stdc++.h>
#define il inline
#define ri register
#define cs const
#define int __int128
using namespace std;
cs int MAXN=15;
il int rd(){
ri int x=0,f=1;
ri char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-f;c=getchar();}
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
if(x>9) wt(x/10);
return putchar(x%10+48),void();
}
il void exgcd(cs int a,cs int b,int &x,int &y){
if(!b) return x=1,y=0,void();
exgcd(b,a%b,y,x);
return y-=x*(a/b),void();
}
il int crt(int a[],int n[],int k){
int x=0,N=1;
for(ri int i=1;i<=k;++i) N*=n[i];
for(ri int i=1,m,inv,y;i<=k;++i){
m=N/n[i];
exgcd(m,n[i],inv,y);
inv=(inv%n[i]+n[i])%n[i];
x=(x+a[i]*m*inv%N)%N;
}
return (x%N+N)%N;
}
signed main(){
int n=0,a[MAXN]={0},b[MAXN]={0};
n=rd();
for(ri int i=1;i<=n;i++){
a[i]=rd(),b[i]=rd();
}
int ans=crt(b,a,n);
wt(ans);
return 0;
}
exCRT
对于\(p_1, p_2 ... p_n\)不一定互质的情况就需要用到 exCRT
结论
两个方程
1 . 设两个方程分别是 \(x\equiv a_1 \pmod {m_1}\)、\(x\equiv a_2 \pmod {m_2}\) ;
2 . 将它们转化为不定方程:\(x=m_1 \cdot p+a_1=m_2 \cdot q+a_2\) ,其中 \(p, q\) 是整数,则有 \(m_1 \cdot p-m_2 \cdot q=a_2-a_1\) 。
3 . 由裴蜀定理,当 \(a_2-a_1\) 不能被 \(\gcd(m_1,m_2)\) 整除时,无解;
4 . 其他情况下,可以通过扩展欧几里得算法解出来一组可行解 \((p, q)\);
5 . 则原来的两方程组成的模方程组的解为 \(x\equiv b\pmod M\),其中 \(b=m_1 \cdot p+a_1,M=\text{lcm}(m_1, m_2)\) 。
多个方程
用上面的方法两两合并即可。
il int excrt(int a[],int b[],int n){
int a1=a[1],b1=b[1];
for(ri int i=2,x,y;i<=n;++i){
exgcd(b1,b[i],x,y);
if((a[i]-a1)%gcd){printf("No solution");return 0;}
x=((x*((a[i]-a1)/gcd))%(b[i]/gcd)+(b[i]/gcd))%(b[i]/gcd);
a1=(a1+x*b1)%(b1*b[i]/gcd),b1*=b[i]/gcd;
}
return a1;
}
code
#include<bits/stdc++.h>
#define il inline
#define ri register
#define cs const
#define F(s) freopen(#s".in","r",stdin),freopen(#s".out","w",stdout);
#define int __int128
using namespace std;
il int rd(){
ri int x=0;
ri char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x;
}
il void wt(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) wt(x/10);
return putchar(x%10+48),void();
}
int gcd;
il void exgcd(cs int a,cs int b,int &x,int &y){
if(!b) return x=1,y=0,gcd=a,void();
return exgcd(b,a%b,y,x),y-=a/b*x,void();
}
il int excrt(int a[],int b[],int n){
int a1=a[1],b1=b[1];
for(ri int i=2,x,y;i<=n;++i){
exgcd(b1,b[i],x,y);
if((a[i]-a1)%gcd){printf("No solution");return 0;}
x=((x*((a[i]-a1)/gcd))%(b[i]/gcd)+(b[i]/gcd))%(b[i]/gcd);
a1=(a1+x*b1)%(b1*b[i]/gcd),b1*=b[i]/gcd;
}
return a1;
}
cs int N=1e5+5;
int n,a[N],b[N];
signed main(){
n=rd();
for(ri int i=1;i<=n;++i) a[i]=rd(),b[i]=rd();
wt(excrt(b,a,n));
return 0;
}
I went to the woods because I wanted to live deliberately, I wanted to live deep and suck out all the marrow of life, and not when I had come to die, discover that I had not live.