中国剩余定理模板
要解决中国剩余定理的问题,分以下几个步骤:
- 会写扩展欧几里得算法
- 会求逆元
- 线性累加
例题
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll arr[15];
ll with[15];
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
else
{
ll d = exgcd(b, a % b, x, y);
ll temp = x;
x = y;
y = temp - a / b * y;
return d;
}
}
ll inv(ll a, ll n)
{
ll x, y;
exgcd(a, n, x, y);
return x;
}
int main()
{
int N;
cin >> N;
ll acc = 1;
for (int i = 0; i < N; i++)
{
cin >> arr[i] >> with[i];
acc *= arr[i];
}
ll np = acc;
ll ans = 0;
for (int i = 0; i < N; i++)
{
acc /= arr[i];
ll iv = inv(acc, arr[i]);
ans = (acc * iv * with[i] + ans) % np;
acc *= arr[i];
}
while (ans < 0)
ans += np;
cout << ans;
return 0;
}
应用
对于模运算,如果底数比较大,我们可以通过CRT进行分解合并。
那么:
( a + b ) m o d n ↔ ( ( a 1 + b 1 ) m o d n , ( a 2 + b 2 ) m o d n , … , ( a k + b k ) m o d n ) ( a − b ) m o d n ↔ ( ( a 1 − b 1 ) m o d n , ( a 2 − b 2 ) m o d n , … , ( a k − b k ) m o d n ) ( a b ) m o d n ↔ ( ( a 1 b 1 ) m o d n , ( a 2 b 2 ) m o d n , … , ( a k b k ) m o d n ) (a+b) \ mod \ n \leftrightarrow ((a_{1}+b_{1}) \ mod \ n,(a_{2}+b_{2}) \ mod \ n,\dots,(a_{k}+b_{k}) \ mod \ n) \\ (a-b) \ mod \ n \leftrightarrow ((a_{1}-b_{1}) \ mod \ n,(a_{2}-b_{2}) \ mod \ n,\dots,(a_{k}-b_{k}) \ mod \ n) \\ (ab) \ mod \ n \leftrightarrow ((a_{1}b_{1}) \ mod \ n,(a_{2}b_{2}) \ mod \ n,\dots,(a_{k}b_{k}) \ mod \ n) \\ (a+b) mod n↔((a1+b1) mod n,(a2+b2) mod n,…,(ak+bk) mod n)(a−b) mod n↔((a1−b1) mod n,(a2−b2) mod n,…,(ak−bk) mod n)(ab) mod n↔((a1b1) mod n,(a2b2) mod n,…,(akbk) mod n)
扩展中国剩余定理(EXCRT)
上述中国剩余定理的解,我们是通过构造解的形式来进行求解,那么当模不互质的时候我们又该怎么办呢?
扩展中国剩余定理又称同余线性方程组通解定理。
我们有一下同余方程组:
x ≡ a 1 m o d m 1 x ≡ a 2 m o d m 2 ⋯ x ≡ a n m o d m n x \equiv a_1 \mod m_1 \\ x \equiv a_2 \mod m_2 \\ \cdots \\ x \equiv a_n \mod m_n x≡a1modm1x≡a2modm2⋯x≡anmodmn
我们考虑第一个方程的通解为 x 1 = a 1 + t m 1 x_1=a_1+tm_1 x1=a1+tm1,那么我们把它带入到第二个方程中,得到:
a 1 + t m 1 ≡ a 2 m o d m 2 t m 1 ≡ a 2 − a 1 m o d m 2 a_1+tm_1 \equiv a_2 \mod m_2 \\ tm_1 \equiv a_2 - a_1 \mod m_2 a1+tm1≡a2modm2tm1≡a2−a1modm2
这是一个线性同余方程,可以使用扩展欧几里得定理求解,假设通解为 t = x 0 + k i t=x_0+ki t=x0+ki,其中 x 0 x_0 x0为特解, k k k为模系数, i i i为迭代变量,我们回代 t t t中,得到: x 2 = a 1 + m 1 x 0 + m 1 k i x_2=a_1+m_1x_0+m_1ki x2=a1+m1x0+m1ki,我们得到了一个变量为 i i i的新的通解,继续带入第三个方程,依次类推,最终我们得到 x n = a + b i x_n=a+bi xn=a+bi的形式,此时的解就是我们想要的解了。
若迭代求解中,其中一个同余方程无解,那么整个同余方程组无解。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
else
{
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
}
ll mul(ll a, ll b, ll p)
{
ll ans = 0;
a = (a % p + p) % p;
b = (b % p + p) % p;
for (; b; b >>= 1, a = (a + a) % p)
{
if (b & 1)
ans = (ans + a) % p;
}
return ans;
}
int main()
{
int N;
scanf("%d", &N);
ll m = 0, n = 1;
while (N--)
{
ll ai, bi;
scanf("%lld %lld", &ai, &bi);
bi = (bi - m) % ai;
ll x, y;
ll d = exgcd(n, ai, x, y);
ll wi = ai / d;
ll x0 = mul(x, bi / d, wi);
ll t = n;
n *= wi;
m = (mul(t, x0, n) + m) % n;
}
m = ((m % n) + n) % n;
printf("%lld", m);
return 0;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define FR freopen("in.txt", "r", stdin)
typedef long long ll;
int n, m;
ll ai[100005];
ll pi[100005];
ll qi[100005];
ll ti[100005];
ll ui[100005];
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
else
{
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
}
ll mul(ll a, ll b, ll p)
{
a = (a % p + p) % p;
b = (b % p + p) % p;
ll ans = 0;
for (; b; b >>= 1, a = (a + a) % p)
{
if (b & 1)
ans = (ans + a) % p;
}
return ans;
}
void excrt()
{
// solve
ll eff = 1;
ll cst = 0;
ll mod = pi[1];
for (int i = 1; i <= n; i++)
{
ll p = pi[i];
ll a = (ui[i] * eff) % p;
ll b = (ai[i] - (ui[i] * cst) % p) % p;
ll x, y;
ll d = exgcd(a, p, x, y);
if (b % d != 0)
{
printf("-1\n");
return;
}
ll Eff = p / d;
ll ef = eff * Eff;
ll Cst = mul(x, b / d, ef);
// ll Cst = (x * (b / d)) % ef;
cst = cst + mul(eff, Cst, ef) % ef;
eff = ef;
}
ll ks = ((cst % eff) + eff) % eff;
for (int i = 1; i <= n; i++)
{
while (ai[i] > ks * ui[i])
ks += eff;
}
printf("%lld\n", ks);
}
int T;
multiset<ll> lis;
void solve()
{
//! input
lis.clear();
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%lld", ai + i);
}
for (int i = 1; i <= n; i++)
{
scanf("%lld", pi + i);
}
for (int i = 1; i <= n; i++)
{
scanf("%lld", qi + i);
}
for (int i = 1; i <= m; i++)
{
scanf("%lld", ti + i);
}
//! match
for (int i = 1; i <= m; i++)
{
lis.insert(ti[i]);
}
for (int i = 1; i <= n; i++)
{
auto it = lis.upper_bound(ai[i]);
if (it != lis.begin())
{
it--;
}
ui[i] = *it;
lis.erase(it);
lis.insert(qi[i]);
}
//! excrt
excrt();
}
int main()
{
scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}