[扩展中国剩余定理(EXCRT)]
前言
进入正题
题目描述
给定 $n$ 组 非负整数 $$a_i,b_i$$,求解关于 $x$ 的方程组
$x ≡ a_1 (mod~b_1)$
$x ≡ a_2 (mod~b_2)$
$.$
$.$
$x ≡ a_n (mod~b_n)$
声明 : $M$ 表示 前 $i-1$ 个$b_i$ 的 最小公倍数$(LCM)$, $ans$ 表示 满足前 $i-1 $个方程的最小解
初始化: 特判 第一组方程 令 $M = b_1 , ans = a_1$(第一组的话 $b_1$的最小公倍数就是自己,只看第一个方程 $ans$ 就等于 $a_1$)
从第二组开始:
$LCM(b_1,b_2) = \frac{b_1×b_2}{gcd(b_1,b_2)}$这个不需要证明
因为 此时 $M = b_1$ ,所以原式可化为:
$LCM(M,b_2) = \frac{M×b_2}{gcd(M,b_2)}$
考虑: $ans + t×M$ 一定是 前 $i-1$ 个方程 的通解,因为 $ans$ 是 最小解 所以 $ans$ 加上前 $i-1$ 个方程 $b_i$ 的最小公倍数 一定是前 $i-1$ 个方程 的通解。(其中 $t ∈ Z$)
所以 如果要使得 $ans$ 也满足 下一个方程的话(以第二个 来举例):
$ans + t×M ≡ a_2 (mod~b_2)$ 此时 $ans$ 是$a_1$ 是已知的
移项得 : $t×M ≡ a_2 - ans (mod~b_2)$(这一看就是 扩展欧几里得)($ax ≡ c (mod~b)$)
所以 此时 $t$ 就相当于是 $x$ ,$M$ 相当于是$a$ ,$a_2 - ans$ 就相当于是$c$
所以利用扩展欧几里得 可以 解得$t$
但是 我们 要考虑 一个问题 扩展欧几里得 的操作
也就是说 我们解得的$t$ 其实是
$t×M ≡ gcd(M,b_2)~(mod~b_2)$的解
所以 $a_2 - ans$ 到 $gcd(M,b_2)$ 相当于是乘了一个 $\frac{gcd(M,b_2)}{a_2 - ans}$ 所以我们得到的解 也相当于是 乘了 一个$\frac{gcd(M,b_2)}{a_2 - ans}$
那么真正的 t 应该是 $\frac{t}{\frac{gcd(M,b_2)}{a_2 - ans}}$
也就是 $t ×\frac{a_2 - ans}{gcd(M,b_2)}$
那么 对于下一次 求解来说
更新答案 : $ans = ans + t × M$,然后 $ans = (ans~mod~M + M)~mod~M$ 保证$ans$ 是最小的正整数解
然后因为 $$LCM(M,b2) = \frac{M×b_2}{gcd(M,b_2)}$$ 所以更新$M$ $$M *= \frac{b_2}{gcd(M,b_2)}$$。
同理 循环 $n - 1$ 次 得到最小的 $ans$ 也就是要求的 $x$
下面贴代码
#include <cstdio> #include <cctype> #include <iostream> #include <algorithm> typedef long long LL; using namespace std; const int maxn = 100005; int n; LL ai[maxn],bi[maxn]; LL quickmul(LL a,LL b,LL mod)// 这是龟速乘 { LL res = 0; while(b) { if(b & 1) res = (res + a) % mod; a = (a + a) % mod; b >>= 1; } return res % mod; } LL exGcd(LL a,LL b,LL &x,LL &y)// 扩欧 { if(!b) { x = 1; y = 0; return a; } LL r = exGcd(b,a%b,x,y); LL t = x; x = y; y = t - a/b * y; return r; } LL exCrt() { LL x,y; LL M = bi[1], ans = ai[1];// 特判 第一组 解 for(int i=2;i<=n;i++) { LL a = M, b = bi[i], c = (ai[i] - ans % b + b) % b;// a*x ≡ c (mod b) LL gcd = exGcd(a,b,x,y); x = quickmul(x,c/gcd,b/gcd);// 相当于 x *= c/gcd ans += x * M; M *= (b / gcd); ans = (ans % M + M) % M;// 找最小的ans } return (ans % M + M) % M; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&bi[i],&ai[i]); printf("%lld",exCrt()); return 0; }