exCRT 浅析

exCRT

处理 CRT\(a_i\) 不互质的情况。
一般使用 exCRT 毕竟其又好想又好写。

\[x =b_1 \pmod {a_1} \\ x =b_2 \pmod {a_2} \\ \cdots \\ x=b_n \pmod {a_n} \]

exCRT 的核心在于维护一个同余方程,并将同余方程不断合并。

假设现在合并了前 \(i-1\) 个方程,不妨设

\[ans=ans_0+k \cdot m , k \in \Z \]

现在要 \(ans'\) 同时满足两个方程

\[ans'=ans_0+k \cdot m\\ ans'=b_i+k_1 \cdot a_i \]

两式相减有

\[k \cdot m -k_1 \cdot a_i=b_i-ans_0 \]

这是一个同余方程,假设其解为
\(k=y + jp,j \in \Z\)
回代 \(ans'=ans_0+(y+jp)m=(ans_0+ym)+j \cdot pm\)
\(ans_0'=ans_0+ym,m'=m \cdot p,ans'=ans_0'+k \cdot m'\)
发现又回到了原来的形式,合并完成。

这个解系满足 \(ans'=(ans_0+ym)+j \cdot pm=ans_0 \pmod m\)
同时也满足第 \(i\) 个方程 \(ans'=ans_0+k \cdot m=b_i+k_1 \cdot a_i\)

接下来证明这样不会漏解


引理 : 两个同余方程在 \(lcm(a_1,a_2)\) 内有唯一解。
证明 : 反证法。
假设存在两个不同的解 \(0 \le x,y <lcm(a_1,a_2)\)
不妨设 \(x >y\)

\[(x-y)=0 \pmod {a_1}\\ (x-y)=0 \pmod {a_2} \]

换句话说

\[a_1 \mid (x-y)\\ a_2 \mid (x-y) \]

则有

\[lcm(a_1,a_2) \mid (x-y) \]

由于 \(x-y <lcm(a_1,a_2)\)
所以 \(x-y=0 ,x=y\) 不符题意。

证毕。


考虑 \(m'=pm\)

\[p={a_i \over \gcd(m,a_i) } \\ m'=pm={a_i\over \gcd(m,a_i) }m={ma_i \over \gcd(m,a_i)}=lcm(m,a_i) \]

因此不会漏解。

在写代码时,直接取 \(m'=lcm(m,a_i)\) 即可。

P4777 【模板】扩展中国剩余定理(EXCRT)
Code:

typedef long long LL;

LL muler(LL x,LL k,LL MOD)
{
	LL res=0; k=(k%MOD+MOD)%MOD; x=(x%MOD+MOD)%MOD; 
	while(k) {
		if(k&1) res=(res+x)%MOD;
		x=(x+x)%MOD; k>>=1;
	}
	return res%MOD;
}

LL exgcd(LL a,LL b,LL& x,LL& y)
{
	if(b==0) {
		x=1; y=0;
		return a;
	}
	LL z=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return z;
}

LL excrt(int n,LL b[],LL a[])
{
	for(int i=1;i<=n;i++) b[i]%=a[i]; // 先把 b[i] mod a[i] 否则 n=1 会错。
	LL m=a[1],ans=b[1]; // 维护方程的解系为 {ans} = ans0 + k*m 
	for(int i=2;i<=n;i++) { // 尝试把第 i 个方程加入解系。 
		LL y,z,d=exgcd(m,a[i],y,z);
		if((b[i]-ans)%d!=0) return -1; // 无法合并同余方程 --> 没有合法解 	
		y=muler(y,(b[i]-ans)/d,a[i]/d); // 同余方程的最小正整数解。 
		
		ans+=y*m;
		m=m/d*a[i]; // m = lcm{a[1]...a[i]} , 不要通过同余方程,这样就很方便,注意先除后乘。 
		ans=(ans%m+m)%m; // 让 ans 为最小正整数。 
	} 
	return ans; 
}

关于标准形式和一般形式的转化。

$x =b_i \pmod {a_i} $ 是标准形式。
\(c_ix=b_i \pmod {a_i}\) 是一般形式。

一般形式不可直接求解,得先 \(\to\) 标准。

考虑先分别求解每一个 \(c_ix=b_i \pmod {a_i}\) ,由于 \(\gcd(c_i,a_i)\) 不一定为 \(1\) ,所以 \(c_i\) 不一定移得过去。

直接求解 \(c_ix+a_ik=b_i\),注意 \(x\) 的解集可以写成

\[x=x_0 \pmod {{a_i \over \gcd(a_i,c_i)}} \]

这是标准形式,对新的方程组求解即可。

若单个方程都没有解,那么总的也没有解。

posted @ 2022-07-05 15:39  cjlworld  阅读(278)  评论(0编辑  收藏  举报