【模板】CRT/EXCRT 中国剩余定理/扩展中国剩余定理

发布时间 2023-03-31 18:07:37作者: caijianhong

problem

求这个关于 \(x\) 的方程组的最小正整数解:

\[\begin{cases} x\equiv b_1 \pmod{a_1}\\ x\equiv b_2 \pmod{a_2}\\ x\equiv b_3 \pmod{a_3}\\ \cdots\\ x\equiv b_n \pmod{a_n}\\ \end{cases}\]

中国剩余定理 CRT

适用范围:\(a_1,a_2,\cdots,a_n\) 两两互质。

做法:令 \(M=\prod a_i,m_i=M/a_i\),然后 \(t_i\)\(m_i\) 在模 \(a_i\) 意义下的逆元。

答案是 \(x=\sum b_im_it_i\)。对 \(M\) 取模。

证明:当 \(x\bmod a_i\) 是,对于不是 \(i\) 的项会因为 \(a_i|m_j\) 变为 \(0\),对于 \(i\) 会因为逆元变为 \(1\)

点击查看代码

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
LL mod(LL x,LL m){return(x%m+m)%m;}
LL exgcd(LL a,LL b,LL c,LL& x,LL& y){
	if(!b) return x=c/a,y=0,a;
	LL res=exgcd(b,a%b,c,y,x);
	return y-=a/b*x,res;
}
LL solve(LL a,LL b,LL c){
	LL x,y,d=exgcd(a,b,c,x,y);
	return c%d==0?mod(x,b/d):-1;
}
int n,a[20],b[20];
LL M=1,ans;
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),M*=a[i];
	for(int i=1;i<=n;i++){
		LL mi=M/a[i];
		ans+=1ll*b[i]*mi*solve(mi,a[i],1);
	}
	printf("%lld\n",ans%M?ans%M:M);

	return 0;
}

扩展中国剩余定理 EXCRT

和 CRT 一点关系没有。

考虑暴力。现在考虑的方程组的 \(a_i\)\(lcm\)\(L\),当前一个合法答案为 \(x\)

加入一个新的方程组,我们不断地使得 \(x\to x+L\),等到 \(x\equiv b_i\pmod{a_i}\) 时停止。

考虑优化,设要加 \(b\) 次,解一个方程:\(x+bL\equiv b_i\pmod{a_i}\to \boxed{a_i}c+\boxed{L}b=\boxed{b_i-x}\)
等式两边有一些系数可以对 \(a_i\) 取模。然后用 exgcd 求出它的最小非负整数解。【模板】exgcd

点击查看代码

模板题很有可能爆 long long。我不想改了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL mod(LL x,LL m){return(x%m+m)%m;}
LL exgcd(LL a,LL b,LL c,LL& x,LL& y){
	if(!b) return x=c/a,y=0,a;
	LL res=exgcd(b,a%b,c,y,x);
	y-=a/b*x;
	return res;
}
LL solve(LL a,LL b,LL c,LL x=0,LL y=0){
	return c%exgcd(a,b,c,x,y)==0?mod(x,b):-1;
}
LL lcm(LL a,LL b){
	LL x,y;
	return a/exgcd(a,b,1,x,y)*b;
}
LL n,ans,tot=1;
bool update(LL a,LL b){
//	while(ans%a!=b) ans+=tot;
//	ans+kt=b(mod a)=>tk+ay=b-ans
	LL z=solve(mod(tot,a),a,mod(b-ans,a));
	if(z==-1) return 0;
	ans+=z*tot,tot=lcm(tot,a),ans=mod(ans,tot);
	return 1;
}
int mian(){
	bool flag=0;
	for(int i=1;i<=n;i++){LL a,b;
		scanf("%lld%lld",&a,&b);
		if(!update(a,b)) flag=1;
	}
	long long x=ans;
	printf("%lld\n",x);
	return 0;
}
void reset(){
	ans=0,tot=1;
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	while(~scanf("%d",&n)) reset(),mian();
	return 0;
}