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

时间:2022-10-15 17:14:47

EXCRT

不保证模数互质

\[\begin{cases} x \equiv b_1\ ({\rm mod}\ a_1) \\ x\equiv b_2\ ({\rm mod}\ a_2) \\ ... \\ x \equiv b_n\ ({\rm mod}\ a_n)\end{cases}
\]

CRT戳这里

来一手数学归纳法

设已经求出前 \(k - 1\) 组的一个解 \(q\)

设 \(M = \prod_{i = 1}^{k - 1}a_{i}\)

我们知道前 \(k - 1\) 组的通解为 \(q + xM\)

现在考虑第 \(k\) 组方程

设存在一 \(x\) 满足

\[q + xM \equiv b_{k}\ (mod\ a_{k})
\]

移一下项

\[xM \equiv b_{k} - q\ (mod\ a_{k})
\]

于是很愉快的解一下同余方程即可

如此, 我们使用扩展欧几里得算法 \(n\) 次, 便求出了方程组的解

复杂度 \(O(n \log n)\)

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

题目描述

给定 n组非负整数 a_i, b_i 求解关于 x的方程组的最小非负整数解。

Solution

注意中间运算会爆 \(longlong\) ,使用龟速乘

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(LL i = (x);i <= (y);i++)
using namespace std;
LL RD(){
LL out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const LL maxn = 200019;
LL num, a[maxn], b[maxn];
LL x, y;
LL Q_mul(LL a, LL b, LL mod){
LL ans = 0;
while(b){
if(b & 1)ans = (ans + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return ans % mod;
}
LL exgcd(LL a, LL b, LL &x, LL &y){
if(!b){x = 1, y = 0;return a;}
LL d = exgcd(b, a % b, x, y);
LL temp = x;x = y;y = temp - (a / b) * y;
return d;
}
LL EXCRT(){
LL M = a[1], ans = b[1];
REP(i, 2, num){
LL A = M, B = a[i], C = ((b[i] - ans) % B + B) % B;
LL d = exgcd(A, B, x, y);
if(C % d != 0) return -1;
x = Q_mul(x, C / d, B / d);
ans += x * M;
M *= B / d;
ans = (ans % M + M) % M;
}
return (ans % M + M) % M;
}
int main(){
num = RD();
REP(i, 1, num)a[i] = RD(), b[i] = RD();
printf("%lld\n", EXCRT());
return 0;
}