中国剩余定理学习笔记
中国剩余定理学习笔记。
前置技能:扩展欧几里得算法。
中国剩余定理
对于这样一个模方程组:
\[\begin{cases}
x=r_1\%m_1 \\
x=r_2\%m_2 \\
...... \\
x=r_n\%m_n
\end{cases}
\]
其中\(m_1,m_2,...m_n\)两两互质。
求\(x\)的最小正整数解。
定理:
有\(M=\prod_{i=1} ^{n}m_i\),因为其两两互质,所以\(M\)为他们的最小公倍数。
\(t_i\)为方程$ \frac M{m_i}*t_i=1(mod,m_i) $的解
很显然可以利用扩展欧几里得解出\(t_i\)
对于当前的模方程,我们只用和上一组通解合并即可。
通解为\(x_i=x_0+i*M\);
代码
#include<bits/stdc++.h>
using namespace std;
int a[15],b[15];
void exgcd(int a,int b,int &d,int &x,int &y) {
if(b)exgcd(b,a%b,d,y,x),y-=(a/b)*x;
else d=a,y=0,x=1;
}
int main() {
int n,ans=0,sum=1,d,y;
scanf("%d",&n);
for(int i=0; i<n; i++)
scanf("%d%d",&a[i],&b[i]);
for(int i=0; i<n; i++) sum*=a[i];
for(int i=0; i<n; i++) {
int w=sum/a[i];
exgcd(a[i],w,d,d,y);
ans=(ans+y*w*b[i])%sum;
}
ans=(ans+sum)%sum;
printf("%d",ans);
}
扩展中国剩余定理
同上,只是\(m_i\)不一定互质。
推导
前\(m\)个方程组的解集为\(Ans+M*K\),现在合并下一个方程。
当前方程为\(x=r_i(mod,m_i)\)
则问题装换为,找到一个最小的\(t\),
满足\(Ans+t*M+m_i*y=r_i\),
即\(t*M+m_i*y=r_i-Ans\);
由于\(M\)和\(m_i\)都是已知量,我们利用扩展欧几里得即可,
解出\(t\),带回合并即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define reg register
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
inline int Read() {
int res=0,f=1;
char c;
while(c=getchar(),c<48||c>57)if(c=='-')f=0;
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>=48&&c<=57);
return f?res:-res;
}
template<class T>inline bool Min(T &a,T const&b) {
return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a,T const&b) {
return a<b?a=b,1:0;
}
const int N=1e5+5;
const int dx[4]= {1,-1,0,0},dy[4]= {0,0,1,-1};
int n,mod[N],res[N];
int Exgcd(int a, int b, int &x, int &y) {
if(!b) {
x=1,y=0;
return a;
}
int g=Exgcd(b,a%b,y,x);
y-=(a/b)*x;
return g;
}
inline int Excrt() {
int M=mod[1],ans=res[1],x,y;
rep(i,2,n){
int g=Exgcd(M,mod[i],x,y);
if((res[i]-ans)%g) return -1;
x*=(res[i]-ans)/g,y=mod[i]/g, x=(x%y+y)%y;
ans=M*x+ans, M=M/g*mod[i], ans%=M;
}
int z=(ans%M+M)%M;
if(!z)z=M;
return z;
}
signed main() {
scanf("%lld",&n);
rep(i,1,n)mod[i]=Read(),res[i]=Read();
int Ans=Excrt();
printf("%lld\n",Ans);
return 0;
}