中国剩余定理
问题
定理
令 M = ∏ i = 1 k m i M=\prod_{i=1}^km_i M=∏i=1kmi,即M是所有 m i m_i mi的最小公倍数
t i t_i ti为同余方程 M m i t i ≡ 1 ( m o d m i ) \frac{M}{m_i}t_i \equiv\ 1(\mod m_i) miMti≡ 1(modmi)的最小非负整数解
则有一个解为 x = ∑ i = 1 k a i M m i t i x=\sum_{i=1}^ka_i\frac{M}{m_i}t_i x=∑i=1kaimiMti
通解为 x + i ∗ M ( i ∈ Z ) x+i*M(i\in Z) x+i∗M(i∈Z)
特别的,最小非负整数解为 ( x (x (x% M + M ) M+M) M+M)% M M M
证明
因为 M m i \frac{M}{m_i} miM是除 m i m_i mi之外的所有m的倍数
所以 ∀ k ≠ i , a i M m i t i ≡ 0 ( m o d m k ) \forall k \not= i, a_i\frac{M}{m_i}t_i \equiv 0(\mod m_k) ∀k=i,aimiMti≡0(modmk)
又有 M m i t i ≡ 1 ( m o d m i ) \frac{M}{m_i}t_i \equiv\ 1(\mod m_i) miMti≡ 1(modmi)
两边同时乘 a i a_i ai得 a i M m i t i ≡ a i ( m o d m i ) a_i\frac{M}{m_i}t_i \equiv a_i(\mod m_i) aimiMti≡ai(modmi)
带入 x = ∑ i = 1 k a i M m i t i x=\sum_{i=1}^ka_i\frac{M}{m_i}t_i x=∑i=1kaimiMti
原方程组成立
——算法竞赛进阶指南
代码实现
t i t_i ti同余式的求解可以用扩展欧几里得
void exgcd(int a,int b,int &x,int &y) {
if(b==0){
x=1; y=0; return;} exgcd(b,a%b,x,y); int tp=x; x=y; y=tp-a/b*y; } int china() {
int ans=0,lcm=1,x,y; for(int i=1;i<=k;++i) lcm*=b[i]; for(int i=1;i<=k;++i) {
int tp=lcm/b[i]; exgcd(tp,b[i],x,y); x=(x%b[i]+b[i])%b[i];//x要为最小非负整数解 ans=(ans+tp*x*a[i])%lcm; } return (ans+lcm)%lcm; }
一道模板题
洛谷P3868 [TJOI2009]猜数字 题解
扩展中国剩余定理
问题
模板题 洛谷P4777 【模板】扩展中国剩余定理(EXCRT)
求解
所以整个算法的思路就是求解k次扩展欧几里得
lt exgcd(lt a,lt b,lt &x,lt &y) {
if(b==0){
x=1;y=0;return a;} lt gcd=exgcd(b,a%b,x,y); lt tp=x; x=y; y=tp-a/b*y; return gcd; } lt excrt() {
lt x,y,k; lt M=bi[1],ans=ai[1];//第一个方程的解特判 for(int i=2;i<=n;i++) {
lt a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;//ax≡c(mod b) lt gcd=exgcd(a,b,x,y),bg=b/gcd; if(c%gcd!=0) return -1; //判断是否无解 x=mul(x,c/gcd,bg); ans+=x*M;//更新前k个方程组的答案 M*=bg;//M为前k个m的lcm ans=(ans%M+M)%M; } return (ans%M+M)%M; }
update
针对一些对代码的疑问做点解答
Q:c=(ai[i]-ans%b+b)%b是什么意思
我们推导出 t ∗ M ≡ a k − x ( m o d m k ) t*M \equiv a_k-x(\mod m_k) t∗M≡ak−x(modmk)
其中 a k − x a_k-x ak−x对应 a x ≡ c ( m o d b ) ax≡c(\mod b) ax≡c(modb)中的c,这句代码里 a n s ans ans对应 x x x, b b b对应 m k m_k mk
所以这句话就是把 a k − x a_k-x ak−x先对 m k m_k mk取模
Q:为啥不加个括号变成c=((ai[i]-ans)%b+b)%b
ans%b的范围是[0,b-1),a[i]-ans%b+b一定不会小于0的,当然加了也没问题
Q:为什么 M ∗ = b g M*=bg M∗=bg而不是 M ∗ = b M*=b M∗=b
这里bg=b/gcd(a,b)对应到推到部分公式里的就是 b g = m k / g c d ( m k , L C M i = 1 k − 1 m i ) bg=m_k/gcd(m_k,LCM_{i=1}^{k-1}m_i) bg=mk/gcd(mk,LCMi=1k−1mi)
M ∗ = b g M*=bg M∗=bg明显就是令M为前k个m的最小公倍数
Q:这句话x=mul(x,c/gcd,bg)为什么要乘c/gcd,为什么模数是bg
建议复习扩展欧几里得
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203418.html原文链接:https://javaforall.net
