中国剩余定理(扩展)
算法简介
中国剩余定理(Chinese Remainder Theorem,CRT)可以用来求解如下形式的一元线性同余方程组。(注意\(n_1,n_2,\cdots,n_k\),两两互质)
基本思路
如果可以找到k个x满足:
那么显然将这k个x加起来就能得到一个满足要求的解。
进一步来看。\(x_i\ \equiv\ 0\ (mod\ n_j)\ \ i\neq j\),这个条件很好满足,只需要令 \(x_i = \frac{n_1n_2\cdots n_k}{n_i}\) 即可,记这个值为 \(m_i\),为了满足 \(x_i\ \equiv\ a_i\ (mod\ n_i)\\\),直接令 \(x_i = a_i\),然而 \(a_im_i\),还不能满足要求,再乘以一个逆元就行啦。
算法流程
1.计算所有模数的积\(N=n_1n_2\cdots n_k\)。
2.对于每个\(x_i\):
\(\quad\)a.计算\(m_i=\frac{N}{n_i}\)。
\(\quad\)b.计算\(m_i在模n_i意义下的逆元m_i^{-1}\)。
3.方程组的解为\(x=\sum_{i=1}^{k}x_i=\sum_{i=1}^{k}a_im_im_i^{-1}(mod\ N)\)。
例题
P1495 【模板】中国剩余定理(CRT)/曹冲养猪
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n;
ll a[20];
ll N=1;
ll exgcd(ll a,ll b,ll&x,ll &y){
if(b==0){
x=1;
y=0;
return a;
}
ll temp;
ll d=exgcd(b,a%b,x,y);
temp=x;
x=y;
y=temp-a/b*y;
return d;
}
ll ans=0;
ll b[20];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%lld%lld",&a[i],&b[i]);
N*=a[i];
}
for(int i=1;i<=n;++i){
ll x,y;
ll d=exgcd(N/a[i],a[i],x,y);
x=(x%a[i]+a[i])%a[i];//注意exgcd求解得到的逆元可能是负的
ans+=b[i]*N/a[i]*x;
ans%=N;
}
printf("%lld",ans);
return 0;
}
扩展
对于刚刚的问题,若模数不严格两两互质,就无法用扩展欧几里得求乘法逆元,所以要使用新的算法。
基本思路
假设我们已经求出前\(k-1\)个方程组的一个解,并且有\(N=LCM_{i=1}^{k-1}n_i\)(及N是前k-1个数的最小公倍数)。
则前k-1个方程的通解为\(x+i*N(i\in Z)\)。
那么对于第k个方程,要找到一个合适的i使\(x+i*N\equiv a_k(mod\ n_k)\)。
其实就是求解\(i*N\equiv a_k-x(mod\ n_k)\),这个时候就又可以用扩展欧几里得求解了。然后再一直往下合并即可。
例题
P4777 【模板】扩展中国剩余定理(EXCRT)
参考代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10;
ll ai[N],bi[N];
int n;
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,x,y);
ll temp=x;
x=y;
y=temp-a/b*y;
return d;
}
ll mul(ll a,ll b,ll mod){
ll ans=0;
while(b){
if(b&1)ans=(ans+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ans;
}
ll excrt(){
ll M=ai[1],ans=bi[1];
for(int i=2;i<=n;++i){
ll x,y,a=M,b=ai[i];
ll c=(bi[i]-ans%b+b)%b;
ll d=exgcd(a,b,x,y);
c=mul(x,c/d,ai[i]/d);
ans+=c*M;
M*=ai[i]/d;
ans=(ans%M+M)%M;
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lld%lld",&ai[i],&bi[i]);
printf("%lld",excrt());
return 0;
}