中国剩余定理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})\)下的逆元
不想证了证明先咕了吧

例题

P1495中国剩余定理/曹冲养猪

#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(虽然这道题的数据保证有解)

例题

P4777扩展中国剩余定理

#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;
} 
posted @ 2021-03-09 17:54  DReamLion  阅读(74)  评论(0编辑  收藏  举报