同余方程(组)、中国剩余定理(CRT、EXCRT)

同余方程

  求出\(ax\equiv b \pmod p\)的解。

  首先根据裴蜀定理必须要有\((a,p)\mid b\),要不然无解。如果\((a,p)=1\),显然\(a\)存在逆元,则\(x \equiv ba^{-1} \pmod p\)

  如果\((a,p)\neq 1\),令\(d=(a,p)\)

\[\because d\mid a, d\mid p, d\mid b \]

\[\therefore 由同余性质得到:\frac{a}{d} x \equiv \frac{b}{d}\pmod{ \frac{p}{d} } \]

  令\(a'=\frac{a}{d}\)\(b'=\frac{b}{d}\)\(p'=\frac{p}{d}\),此时有\((a', p')=1\)\(a'\)存在逆元,所以\(x \equiv b'a'^{-1} \pmod {p'}\)。如果逆元不能用费马定理求解(比如说\(p'\)不是质数),就用\(\text{Ex-GCD}\)求解。一般也就用这个方法求。

  对于同余方程组呢?比如说求解

\[\begin{cases} x \equiv a_1 \pmod {p_1} \\ x \equiv a_2 \pmod {p_2} \\ \dotsb \\ x \equiv a_n \pmod {p_n} \\ \end{cases} \]

  这里需要用到中国剩余定理(CRT)了。

解法一:构造

  让\(M=\prod p_i\)\(m_i = \frac{M}{p_i}\),令\(m_i^{-1}\)\(m_i\)在模\(p_i\)意义下的逆元,则有\(m_im_i^{-1} \equiv 1 \pmod {p_i}\),即\(a_im_im_i^{-1} \equiv a_i \pmod {p_i}\),将\(a_im_im_i^{-1}\)记作\(L_i\)。这里可以看出\(m_i^{-1}\)的存在必须满足:\((m_i,p_i)=1\),也就是\(p_i\)两两互质才行。

  构造\(x=\sum L_i\)\(x \bmod M\)就是同余方程组的最小正整数解。

  \(\forall i,j:L_j \bmod p_i \equiv \begin{cases} 0,j \neq i(m_i中有因子p_i)\\ a_i,j=i(由上面定义可知)\end{cases}\)

ll crt(int n) {
    ll M = 1, ans = 0;
    rep(i, 1, n) M *= b[i];
    rep(i, 1, n) {
        ll x, y, d = exgcd(m[i] = M/b[i], b[i], x, y);
        ans = (ans+1ll*a[i]*m[i]*x) % M; // 上文公式
    }
    return ans < 0 ? ans+M : ans; // 答案为正数
}

  构造的局限性也很显然:模数要两两互质才行,否则无法构造。

  复杂度:\(\mathcal O(n\log (\prod p_i))\),因为要求逆。

解法二:合并

  又称扩展中国剩余定理(EXCRT)。

  构造可以应用于各种情形(不互质也ok)。思想就是将两个同余式合并成一个同余式,合并\(n-1\)次只剩下一个同余式,就是我们要的答案。

  令\(k_i=\text{LCM}_{x\leq i}\ p_x\),假设我们合并了前面的\(i-1\)个方程,通解为\(x\),也就是说这个\(x\)代入前\(i-1\)个方程恒成立,为了让第\(i\)个方程成立,我们有:

\[x+t\times k_{i-1}\equiv a_i \pmod {p_i} \]

  记\(x'=x+t\times k_{i-1}\),发现\(x'\)对于前\(i\)个式子恒成立,而且\(k_{i-1}\)与前\(i-1\)个模数取余为\(0\)。解出来\(t\),然后一直算下去。

  但是不互质时可能会无解,当且仅当\((k_{i-1}, p_i)\nmid a_i-x\)

// 变量有所区别
ll excrt(int n) {
    ll lcm = 1, ans = 0; // lcm为前i个数的最小公倍数,ans为答案
    rep(i, 1, n) {
        ll x, y, d = exgcd(lcm, b[i], x, y); // 求解约分后的同余方程,其中x为解,d为最大公约数
        if ((a[i]-ans) % d) return -1; // 判断原来的同余方程是否有解
        ll t = b[i]/d; // 约分后的b
        x = x * (a[i]-ans)/d % t; // 还原x的解
        ans += x * lcm; ans %= (lcm *= t); // 合并结果,维护lcm
    }
    return ans < 0 ? ans+lcm : ans; // 注意答案要为正数
}
// 注意中间乘积和取模可能会溢出,需要用玄学快速乘

  复杂度\(\mathcal O(n\log(\text{LCM}_{1\leqslant i\leqslant n}p_i))\)

posted @ 2020-04-12 13:51  AC-Evil  阅读(489)  评论(0编辑  收藏  举报