扩展中国剩余定理模板

{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) \begin{cases}x \equiv a_1 \pmod{m_1} \\x \equiv a_2 \pmod{m_2} \end{cases} {xa1(modm1)xa2(modm2)

{ x = a 1 + k 1 m 1 x = a 2 + k 2 m 2 \begin{cases}x=a_1+k_1m_1 \\x=a_2+k_2m_2 \end{cases} {x=a1+k1m1x=a2+k2m2

k 1 m 1 + ( − k 2 ) m 2 = a 2 − a 1 k_1m_1+(-k_2)m_2=a_2-a_1 k1m1+(k2)m2=a2a1

d = gcd ⁡ ( m 1 , m 2 ) , e = a 2 − a 1 d=\gcd(m_1,m_2), e=a_2-a_1 d=gcd(m1,m2),e=a2a1

使用扩展欧几里得算法求解 x , y x,y x,y ,满足 x m 1 + y m 2 = d xm_1+ym_2=d xm1+ym2=d

则有 x ⋅ e d ⋅ m 1 + y ⋅ e d ⋅ m 2 = d ⋅ e d x \cdot \frac{e}{d} \cdot m_1+y \cdot \frac{e}{d} \cdot m_2=d \cdot \frac{e}{d} xdem1+ydem2=dde

对比可得 { k 1 = x ⋅ e d k 2 = − y ⋅ e d \begin{cases}k_1=x \cdot \frac{e}{d} \\k_2=-y \cdot \frac{e}{d} \end{cases} {k1=xdek2=yde

实际上有多组解 { k 1 = x ⋅ e d + m 2 d ⋅ n k 2 = − y ⋅ e d − m 1 d ⋅ n , n ∈ Z \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} {k1=xde+dm2nk2=ydedm1n,nZ

k 1 k_1 k1 的最小整数解, { A = a 1 + k 1 m 1 M = l c m ( m 1 , m 2 ) = m 1 m 2 d \begin{cases}A=a_1+k_1m_1 \\M=lcm(m_1,m_2)=\frac{m_1m_2}{d} \end{cases} {A=a1+k1m1M=lcm(m1,m2)=dm1m2

合并成了 x ≡ A ( m o d M ) x\equiv A \pmod{M} xA(modM)


对于有些题目,中间运算结果会爆long long。不要偷懒使用__int128,下面是错误示范 ,乖乖打龟速乘。(也不要手写__int128)

#include
#include
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),mm=m[i]/d;
		if ((a[i]-A)%d) {puts("No solution!");return;}
		x=(x%mm+mm)%mm;
		LL k=(LL)((__int128)(a[i]-A)/d*x%mm+mm)%mm;
		LL nM=M*mm;
		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;
}

你可能感兴趣的:(模板,数学)