中国剩余定理CRT&扩展中国剩余定理EXCRT
中国剩余定理
解法
\[\left\{\begin{matrix}
\\x \equiv b_{1}(mod \ a_{1})
\\x \equiv b_{2}(mod \ a_{2})
\\...
\\x \equiv b_{n}(mod \ a_{n})
\end{matrix}\right.
\]
其中\(a_{i}\)互质,求\(x\)
结论:在\(lcm(\prod a_{i})\)的剩余系下,有唯一解:
\[\sum a_{i} \times M_{i} \times M^{-1}_{i}
\]
其中,\(M_{i}=\prod_{j\ne i }^{} a_{j}\),\(M_{i}^{-1}\)为\(M_{i}\)在\((mod \ a_{i})\)下的逆元
不想证了证明先咕了吧
例题
#include<bits/stdc++.h>
#define ll long long
#define maxn 15
using namespace std;
ll n,a[maxn],b[maxn];
ll m=1,ans=0;
ll exgcd(ll a,ll b,ll &x,ll &y) {
if(!b){
x=1,y=0;
return a;
}
ll d=exgcd(b,a%b,y,x);
y=y-(a/b)*x;
return d;
}
signed int main(){
cin>>n;
for(ll i=1;i<=n;i++) {
cin>>a[i]>>b[i];
m*=a[i];
}
for(ll i=1;i<=n;i++){
ll x=0,y=0;
exgcd(m/a[i],a[i],x,y);
ans=((ans+(m/a[i])*b[i]*x)%m+m)%m;
}
cout<<ans%m<<endl;
return 0;
}
扩展中国剩余定理
解法
用于处理模数不互质的情况
思路是两两合并,最后转化为求\(k\)次扩展欧几里得,得到式子:
\[x=X+k \times lcm(a1+a2)
\]
即为
\[x \equiv X \ mod(lcm(a_{1},a_{2})
\]
至于过程中的合并什么的,就是先用EXGCD把两个方程合并成一个方程放进方程组里:
\[x \equiv b_{1}(mod \ a_{1}),x \equiv b_{2}(mod \ a_{2})
\]
得到:
\[x=k_{1}a_{1}+b_{1}=k_{2}a_{2}+b_{2}
\]
移项:
\[k_{1}a_{1}+k_{2}a_{2}=b_{2}-b_{1}
\]
解出:
\[k_{1}a_{1}+k_{2}a_{2} \equiv gcd(a_{1},a_{2})
\]
有解条件为\(gcd(a_{1},a_{2})|b_{2}-b_{1}\)
然后一直这样合并合并,直到只剩下一个方程,哦如果合并的过程中无解了那就返回-1(虽然这道题的数据保证有解)
例题
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define maxn 100010
#define ll long long
using namespace std;
template<typename T>
inline void read(T &x){
x=0; bool flag=0; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
if(flag) x=-x;
}
ll n,a[maxn],b[maxn];
ll qmul(ll a,ll b,ll p){
ll res=0;
for(;b;b>>=1,a=(a+a)%p) if(b&1) res=(res+a)%p;
return res%p;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;y=0;
return a;
}
ll d=exgcd(b,a%b,y,x);
y=y-(a/b)*x;
return d;
}
ll excrt(ll n,ll *a,ll *b){
ll x,y,k,tmp;
ll ans=a[1],m=b[1];
for(int i=2;i<=n;i++){
k=exgcd(m,b[i],x,y);
tmp=((a[i]-ans)%b[i]+b[i])%b[i];
if(tmp%k!=0) return -1; //判断是否无解
ans+=qmul(x,tmp/k,b[i])*m;
m*=b[i]/k;
ans=(ans%m+m)%m;
}
return ans;
}
int main(){
read(n);
for(int i=1;i<=n;i++)
read(b[i]),read(a[i]);//注意把读入反过来了(要不调了半天找不出来错......
cout<<excrt(n,a,b);
return 0;
}