1. 程式人生 > >Luogu 4777 【模板】擴展中國剩余定理(EXCRT)

Luogu 4777 【模板】擴展中國剩余定理(EXCRT)

name space return 同余 pan 自己 pre 圖片 是把

復習模板。

兩兩合並同余方程

$x\equiv C_{1} \ (Mod\ P_{1})$

$x\equiv C_{2} \ (Mod\ P_{2})$

把它寫成不定方程的形式:

$x = C_{1} + P_{1} * y_{1}$

$x = C_{2} + P_{2} * y_{2}$

發現上下兩式都等於$x$

所以$C_{1} + P_{1} * y_{1} = C_{2} + P_{2} * y_{2}$

稍微移項一下,有$P_{1} * y_{1} + P_{2} * (-y_{2}) = C_{2} - C_{1}$。

發現這個式子很眼熟,其實就是一個不定方程,那麽根據裴蜀定理,要使此方程有解需要滿足$gcd(P_{1}, P_{2}) | (C_{2} - C_{1})$,否則這一堆同余方程就無解了。

我們有$exgcd$算法可以解這個$y_{1}$,解出來之後把它回代到上式裏面去,就得到了合並之後的同余方程:$x\equiv C_{1} + P_{1} * y_{1} \ (Mod\ lcm(P_{1}, P_{2}))$。

根據【NOI2018】屠龍勇士的經驗,當$P == 1$的時候,這個同余方程其實是沒什麽意義的,但是把它代進去算就會掛掉,所以需要特判掉。

發現乘法會溢出,需要使用龜速乘,按照我自己的sb寫法,要註意在龜速乘的時候保證$y \geq 0$。

時間復雜度$O(nlog^{2}n)$,然而歐幾裏得算法的$log$基本上都跑不滿。

Code:

技術分享圖片
#include <cstdio>
#include 
<cstring> using namespace std; typedef long long ll; const int N = 1e5 + 5; int n; ll rest[N], mod[N]; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > 9 || ch < 0; ch = getchar()) if(ch == -) op = -1;
for(; ch >= 0 && ch <= 9; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline ll mul(ll x, ll y, ll P) { ll res = 0; for(; y > 0; y >>= 1) { if(y & 1) res = (res + x) % P; x = (x + x) % P; } return res; } ll exgcd(ll a, ll b, ll &x, ll &y) { if(!b) { x = 1, y = 0; return a; } ll res = exgcd(b, a % b, x, y), tmp = x; x = y, y = tmp - (a / b) * y; return res; } inline ll exCrt() { ll P, c, x, y, d, t; int pos = 0; for(int i = 1; i <= n; i++) if(mod[i] != 1) { pos = i, P = mod[i], c = rest[i]; break; } for(int i = pos + 1; i <= n; i++) { if(mod[i] == 1) continue; d = exgcd(P, mod[i], x, y); ll r = (((rest[i] - c)) % mod[i] + mod[i]) % mod[i]; t = mul(x, r / d, mod[i] / d); // t = (rest[i] - c) / d * x; // t = (t % (mod[i] / d) + (mod[i] / d)) % (mod[i] / d); // c = (c + mul(P, t, P / d * mod[i])) % (P / d * mod[i]); c = c + P * t; P = P / d * mod[i]; c = (c % P + P) % P; } return (c % P + P) % P; } int main() { read(n); for(int i = 1; i <= n; i++) read(mod[i]), read(rest[i]); printf("%lld\n", exCrt()); return 0; }
View Code

Luogu 4777 【模板】擴展中國剩余定理(EXCRT)