中国剩余定理与扩展中国剩余定理
概述
中国剩余定理是同余中常用的辅助手段,常用来合并多个同余方程的解(如MTT)。其技术难度不高,代码也很短,可以在NOIP中出现。
中国剩余定理CRT
形式
给定 (n) 组非负整数 (m_i, a_i) ,求解关于 (x) 的方程组的最小非负整数解。
[egin{cases} x equiv a_1 ({
m mod} m_1) \ xequiv a_2 ({
m mod} m_2) \ ... \ x equiv a_n ({
m mod} m_n)end{cases}
]
其中(m_i)两两互质。
解法
我们贪心构造和式使得每一项在模意义下不影响其他项,并且符合当前的方程,
设(M =prod^{n}_{i=1}m_i,M_i=frac{M}{m_i}),可以构造解
[x=sum^{n}_{i=1}M_i(a_iM_i^{-1} mod m_i)
]
可以证明解在模M 意义下唯一。
模板题:P3868 [TJOI2009]猜数字
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=11;
inline LL mult(LL a,LL b,LL mod){
LL ret=0;
while(b>0){
if(b&1)
ret=(ret+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ret;
}
void exgcd(LL a,LL b,LL &x,LL &y){
if(!b){
x=1;
y=0;
return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
inline LL inv(LL x,LL p){
LL a,b;
exgcd(x,p,a,b);
return (a%p+p)%p;
}
int N;
LL a[MAXN],b[MAXN],M,lcm=1,ans;
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%lld",a+i);
for(int i=1;i<=N;i++){
scanf("%lld",b+i);
lcm*=b[i];
a[i]=(a[i]%b[i]+b[i])%b[i];
}
for(int i=1;i<=N;i++){
LL Mi=lcm/b[i],t=inv(Mi,b[i]);
ans=(ans+mult(mult(Mi,t,lcm),a[i],lcm))%lcm;
}
printf("%lld",(ans+lcm)%lcm);
return 0;
}
扩展中国剩余定理EXCRT
形式
给定 (n) 组非负整数 (m_i, a_i) ,求解关于 (x) 的方程组的最小非负整数解。
[egin{cases} x equiv a_1 ({
m mod} m_1) \ xequiv a_2 ({
m mod} m_2) \ ... \ x equiv a_n ({
m mod} m_n)end{cases}
]
解法
扩展中国剩余定理的方法和中国剩余定理关系不大,我们贪心处理前(i-1)项,然后合并当前方程。
将每个方程拆成若干个方程
[x=a_i(mod p_{i,j}^{k_{i,j}})
]
其中(m_i=prod p_{i,j}^{k_{i,j}})为(m_i) 的分解式。对每个质数(p),合并对应的所有方程,从而转化为模数两两互质的情形。若合并过程中出现矛盾,则原方程组无解。
模板题:P4777 【模板】扩展中国剩余定理(EXCRT)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=1e5+10;
inline LL mult(LL a,LL b,LL mod){
LL ret=0;
while(b>0){
if(b&1)
ret=(ret+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ret;
}
LL exgcd(LL a,LL b,LL &x,LL &y){
if(!b){
x=1;
y=0;
return a;
}
LL ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
int N;
LL a[MAXN],b[MAXN],M,ans,x,y;
int main(){
scanf("%d",&N);
for(int i=1;i<=N;i++){
scanf("%lld%lld",b+i,a+i);
a[i]=(a[i]%b[i]+b[i])%b[i];
}
M=b[1];
ans=a[1];
for(int i=2;i<=N;i++){
LL rm=(a[i]-ans%b[i]+b[i])%b[i];
LL gcd=exgcd(M,b[i],x,y);
LL bg=b[i]/gcd;
/*if(rm%gcd):ERROR*/
x=mult(x,rm/gcd,bg);
ans+=x*M;
M*=bg;
ans=(ans%M+M)%M;
}
printf("%lld",ans);
return 0;
}