反正是复习,我长话短说只写这玩意重点了.
CRT
题目
给定 n n n 组非负整数 m i , a i m_i, a_i m i , a i ,求解关于 x x x 的方程组的最小非负整数解。
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) … x ≡ a n ( m o d m n ) \begin{cases}
x \equiv a_1 \pmod{m_1} \\
x \equiv a_2 \pmod{m_2} \\
\dots \\
x \equiv a_n \pmod{m_n}
\end{cases}
⎩ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎧ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) … x ≡ a n ( m o d m n )
对于 100 % 100 \% 1 0 0 % 的数据,1 ≤ n ≤ 10 5 1 \le n \le {10}^5 1 ≤ n ≤ 1 0 5 ,1 ≤ m i ≤ 10 12 1 \le m_i \le {10}^{12} 1 ≤ m i ≤ 1 0 1 2 ,0 ≤ a i ≤ 1 0 12 0\leq a_i \leq 10^{12} 0 ≤ a i ≤ 1 0 1 2 ,保证所有 m i m_i m i 的最小公倍数不超过 10 18 {10}^{18} 1 0 1 8 ,保证 m m m 中的元素两两互质。
数据保证有解。
思路
设 M = ∏ m i M = \prod m_i M = ∏ m i 。
我们对于每个 i ( 1 ≤ i ≤ n ) i \left (1 \leq i \leq n \right ) i ( 1 ≤ i ≤ n ) ,算出这样的一个 x i x_i x i 满足:
M x i m i ≡ 1 ( m o d m i ) \frac{Mx_i}{m_i} \equiv 1 \pmod{m_i}
m i M x i ≡ 1 ( m o d m i )
可以用括欧做出来,然后答案就是:
∑ i = 1 n a i x i \sum_{i = 1} ^ n a_ix_i
i = 1 ∑ n a i x i
原理大概就是当前方程的x i x_i x i 满足模其他几个方程都是 0 0 0 ,我们算出模当前模数为 1 1 1 的结果后就满足了当前的方程,最后一个一个加起来就全满足了。
代码
原题 曹冲养猪会爆ll,所以代码里开了__int128
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <bits/stdc++.h> using namespace std;#define int __int128 int gcd (int x, int y) { return y == 0 ? x : gcd (y, x % y); } int lcm (int x, int y) { return x / gcd (x, y) * y; } void exgcd (int a, int b, int &x, int &y) { if (b == 0 ) { x = 1 ; y = 0 ; return ; } exgcd (b, a % b, x, y); int tp = x; x = y; y = tp - (a / b) * y; } pair<int , int > solveeqn (int a, int b, int k) { if (k % gcd (a, b)) { return {-1 , -1 }; } int x0, y0; exgcd (a, b, x0, y0); int g = gcd (a, b); int tp = k / g; x0 *= tp; y0 *= tp; while (x0 < 0 ) { x0 += b / g; y0 -= a / g; } return {x0, y0}; } signed main () { long long n; cin >> n; vector<long long > a (n) , m (n) ; int s = 1 ; for (int i = 0 ; i < n; i++) { cin >> m[i] >> a[i]; s *= m[i]; } int ans = 0 ; for (int i = 0 ; i < n; i++) { int tp = solveeqn (s / m[i], m[i], 1 ).first; if (tp == -1 ) { cout << -1 << '\n' ; return ; } ans += a[i] * s / m[i] * tp; } ans %= s; if (ans < 0 ) { ans += s; } cout << (long long )ans << '\n' ; return 0 ; }
EXCRT
题目
给定 n n n 组非负整数 m i , a i m_i, a_i m i , a i ,求解关于 x x x 的方程组的最小非负整数解。
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) … x ≡ a n ( m o d m n ) \begin{cases}
x \equiv a_1 \pmod{m_1} \\
x \equiv a_2 \pmod{m_2} \\
\dots \\
x \equiv a_n \pmod{m_n}
\end{cases}
⎩ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎧ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) … x ≡ a n ( m o d m n )
对于 100 % 100 \% 1 0 0 % 的数据,1 ≤ n ≤ 10 5 1 \le n \le {10}^5 1 ≤ n ≤ 1 0 5 ,1 ≤ m i ≤ 10 12 1 \le m_i \le {10}^{12} 1 ≤ m i ≤ 1 0 1 2 ,0 ≤ a i ≤ 1 0 12 0\leq a_i \leq 10^{12} 0 ≤ a i ≤ 1 0 1 2 ,保证所有 m i m_i m i 的最小公倍数不超过 10 18 {10}^{18} 1 0 1 8 。
数据保证有解。
思路
现在里面的数不两两互质了,所以上面的方法是行不通的。
我们先考虑只有两个同余方程的情况:
{ x ≡ a ( m o d b ) x ≡ c ( m o d d ) \begin{cases}
x \equiv a \pmod{b} \\
x \equiv c \pmod{d}
\end{cases}
{ x ≡ a ( m o d b ) x ≡ c ( m o d d )
这里 x x x 就能表示成 b × p + a b \times p + a b × p + a 的形式,也可以表示成 d × q + c d \times q + c d × q + c 的形式。
所以我们就能得到关于 p , q p, q p , q 的一个不定方程:
b × p − d × q = a − c b \times p - d \times q = a - c
b × p − d × q = a − c
扩展欧几里得就可以把 p p p 和 q q q 这两个未知数解出来。
那么很容易就能把这两个方程合并为一个方程:
x ≡ b × p + a ( m o d lcm ( b , d ) ) x \equiv b \times p + a \pmod{ \operatorname{lcm}(b, d) }
x ≡ b × p + a ( m o d l c m ( b , d ) )
然后一直合并下去就做完了,喵。
#include <bits/stdc++.h>
using namespace std;
#define int long long
void exgcd (__int128 a, __int128 b, __int128 &x, __int128 &y)
{
if (b == 0 ) {
x = 1 ;
y = 0 ;
return ;
}
exgcd (b, a % b, x, y);
__int128 tp = x;
x = y;
y = tp - (a / b) * y;
}
__int128 gcd (__int128 x, __int128 y)
{
return y == 0 ? x : gcd (y, x % y);
}
__int128 lcm (__int128 x, __int128 y)
{
return x / gcd (x, y) * y;
}
pair<__int128, __int128> solveeqn (__int128 a, __int128 b, __int128 k)
{
if (k % gcd (a, b)) {
return {-1 , -1 };
}
__int128 x0, y0;
exgcd (a, b, x0, y0);
__int128 g = gcd (a, b);
__int128 tp = k / g;
x0 *= tp;
y0 *= tp;
if (x0 < 0 ) {
int tp = (0 - x0 + b / g - 1 ) / (b / g);
x0 += tp * (b / g);
y0 -= tp * (b / g);
}
return {x0, y0};
}
signed main ()
{
int n;
cin >> n;
vector<int > a (n) , m (n) ;
for (__int128 i = 0 ; i < n; i++) {
cin >> m[i] >> a[i];
}
__int128 tpa = a[0 ], tpm = m[0 ];
for (__int128 i = 1 ; i < n; i++) {
auto tp = solveeqn (tpm, -m[i], tpa - a[i]);
tpa -= tpm * tp.first;
tpm = lcm (tpm, (__int128)m[i]);
tpa = (tpa + tpm) % tpm;
if (tpa < 0 ) {
tpa += tpm;
}
}
cout << (int )tpa;
return 0 ;
}