擴充套件中國剩餘定理(EXCRT)學習筆記

liuchanglc 發表於 2021-04-07

擴充套件中國剩餘定理(EXCRT)學習筆記

用途

求解同餘方程組

\(\begin{cases}x\equiv c_{1}\left( mod\ m_{1}\right) \\ x\equiv c_{2}\left( mod\ m_{2}\right) \\ \ldots \\ x\equiv c_r\left( mod\ m_r\right) \end{cases}\)

其中 \(m_1,m_2,m_3...m_k\) 為不一定兩兩互質的整數, 求 \(x\) 的最小非負整數解。

求法

考慮兩兩合併同餘方程,使得新得到的同餘方程滿足之前兩個同餘方程的限制條件。

這樣合併到最後只剩下一個同餘方程直接輸出答案即可。

對於兩個同餘方程 \(x\%a_1=b_1,x\%a_2=b_2\)

\(x=k_1a_1+b_1,x=k_2a_2+b_2\)

那麼有 \(k_1a_1+b_1=k_2a_2+b_2\)

\(k_1a_1-k_2a_2=b_2-b_1\)

\(d=gcd(a_1,a_2)\),如果 \((b_2-b_1)\%d \ne 0\),那麼無解。

否則對於方程兩邊同時除以 \(d\)

\(k_1\frac{a_1}{d}-k_2\frac{a_2}{d}=\frac{b_2-b_1}{d}\),

\(k_1\frac{a_1}{d}=\frac{b_2-b_1}{d}+k_2\frac{a_2}{d}\)

可以看作 \(k_1\frac{a_1}{d}\%\frac{a_2}{d}=\frac{b_2-b_1}{d}\)

\(inv=\frac{a_1}{d} \% \frac{a_2}{d}\) 意義下的逆元,

對於方程兩邊同時乘 \(inv\)

\(k_1\%\frac{a_2}{d}=\frac{inv(b_2-b_1)}{d}\)

展開後有 \(k_1=\frac{inv(b_2-b_1)}{d}+k_2\frac{a_2}{d}\)

代入開始的 \(x=a_1k_1+b_1\)

\(x=\frac{a_1inv(b_2-b_1)}{d}+k_2\frac{a_1a_2}{d}+b_1\)

\(\frac{a_1a_2}{d}\) 看成新的 \(a\),把 \(\frac{a_1inv(b_2-b_1)}{d}+b_1\) 看成新的 \(b\) 即可。

程式碼

【模板】擴充套件中國剩餘定理(EXCRT)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=1e5+5;
int n;
long long a[maxn],b[maxn];
long long gcd(rg long long aa,rg long long bb){
	return bb==0?aa:gcd(bb,aa%bb);
}
long long lcm(rg long long aa,rg long long bb){
	return aa/gcd(aa,bb)*bb;
}
long long exgcd(rg long long aa,rg long long bb,rg long long&xx,rg long long&yy){
	if(bb==0){
		xx=1,yy=0;
		return aa;
	}
	rg long long nans=exgcd(bb,aa%bb,xx,yy);
	rg long long t=xx;
	xx=yy;
	yy=t-aa/bb*yy;
	return nans;
}
long long getinv(rg long long val,rg long long mod){
	rg long long xx,yy;
	exgcd(val,mod,xx,yy);
	return (xx%mod+mod)%mod;
}
long long gsc(rg long long ds,rg long long zs,rg long long mod){
    return ((unsigned long long)(ds*zs)-(unsigned long long)((long double)ds/mod*zs)*mod+mod)%mod;
}
int main(){
	read(n);
	for(rg int i=1;i<=n;i++) read(a[i]),read(b[i]);
	rg long long newa,newb,tmp;
	for(rg int i=2;i<=n;i++){
		newa=lcm(a[i],a[i-1]),tmp=gcd(a[i],a[i-1]);
		newb=gsc((b[i]-b[i-1])/tmp,getinv(a[i-1]/tmp,a[i]/tmp),newa);
		newb=gsc(newb,a[i-1],newa)+b[i-1];
		newb=(newb%newa+newa)%newa;
		a[i]=newa,b[i]=newb;
	}
	printf("%lld\n",b[n]);
	return 0;
}