1. 程式人生 > >擴充套件中國剩餘定理模板

擴充套件中國剩餘定理模板

{xa1(modm1)xa2(modm2)\begin{cases}x \equiv a_1 \pmod{m_1} \\x \equiv a_2 \pmod{m_2} \end{cases}

{x=a1+k1m1x=a2+k2m2\begin{cases}x=a_1+k_1m_1 \\x=a_2+k_2m_2 \end{cases}

k1m1+(k2)m2=a2a1k_1m_1+(-k_2)m_2=a_2-a_1

d=gcd(m

1,m2),e=a2a1d=\gcd(m_1,m_2), e=a_2-a_1

使用擴充套件歐幾里得演算法求解 x,yx,y ,滿足 xm1+ym2=dxm_1+ym_2=d

則有xedm1+yedm2=dedx \cdot \frac{e}{d} \cdot m_1+y \cdot \frac{e}{d} \cdot m_2=d \cdot \frac{e}{d}

對比可得{k1=xedk2=yed\begin{cases}k_1=x \cdot \frac{e}{d} \\k_2=-y \cdot \frac{e}{d} \end{cases}

實際上有多組解{k1=xed+m2dnk2=yedm1dn,nZ\begin{cases}k_1=x \cdot \frac{e}{d}+\frac{m_2}{d} \cdot n \\k_2=-y \cdot \frac{e}{d}-\frac{m_1}{d} \cdot n\end{cases} , n\in \mathbb{Z}

k1k_1 的最小整數解,{A=a1+k1m1M=lcm(m1,m2)=m1m2d\begin{cases}A=a_1+k_1m_1 \\M=lcm(m_1,m_2)=\frac{m_1m_2}{d} \end{cases}

合併成了 xA(modM)x\equiv A \pmod{M}

對於有些題目,中間運算結果會爆long long。不要偷懶使用__int128,下面是錯誤示範 ,乖乖打龜速乘。(也不要手寫__int128)

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
LL a[100005],m[100005];
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,y,x);y-=(a/b)*x;
	return d;
}
inline void excrt() {
	LL M=1,A=0;
	for (int i=1;i<=n;i++) {
		LL x,y,d=exgcd(M,m[i],x,y),tmp=m[i]/d;
		if ((a[i]-A)%d) {puts("No solution!");return;}
		x=(x%tmp+tmp)%tmp;
		LL k=(LL)((__int128)(a[i]-A)/d*x%tmp+tmp)%tmp;
		LL nM=M*tmp;
		A=(LL)((A+(__int128)k*M)%nM);
		M=nM;
	}
	printf("%lld",A);
}
int main() {
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%lld %lld",&m[i],&a[i]);
	excrt();
	return 0;
}