CRT 和 EXCRT
CRT
题目问题:解方程
解法:
M = \sum_{i = 1} ^ n m_i$, $M_i = \frac{M}{ m_i} 可以发现
M_i M_i^{-1} \equiv 1 \pmod {m_i} 诶,我们发现
a \times b\mod c = a \mod c \times b \mod c 所以我们可以将 LHS 和 RHS 同时乘以
a_i 这样就可以构造出一个合法的解所以
ans = \sum_{i = 1} ^ n {M_iM_i^{-1}a_i} 因为取最小正整数解,所以只要
\mod M 就可以了
之后我们考虑逆元应该怎么求,可以用费马小定理,但是觉得应该学学
我们来看看 exgcd,可以解
所以我们应该变换
第一次自己推出来, Happy!
也就是
P1495 中国剩余定理(CRT)
#include <bits/stdc++.h>
using namespace std;
template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for(; isdigit(c);c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= f;
}
#define int long long
const int maxn = 2e5 + 5;
const int maxm = maxn << 1;
typedef int room[maxn];
int n, m, M(1);
int mi[maxn], a[maxn], Mi[maxn];
int ans(0);
void exgcd(int a,int b,int &x,int &y) {
if(b == 0) return x = 1, y = 0, void();
exgcd(b, a % b, x, y);
int z = x; x = y, y = z - y * (a / b);
// 注意 y 的处理顺序,需要 floor(a / b) 所以不能让 y 的因子被除
}
signed main() {
int i, j;
r1(n);
for(i = 1;i <= n; ++ i) {
r1(mi[i]), r1(a[i]);
M *= mi[i];
}
for(i = 1;i <= n; ++ i) {
Mi[i] = M / mi[i];
int x(0), y(0);
exgcd(Mi[i], mi[i], x, y);
x = (x + mi[i]) % mi[i];
ans += Mi[i] * x * a[i] % M;
}
printf("%lld\n", ans % M);
return 0;
}
EXCRT
题目意思:解方程
我们考虑对于一个方程组,假设已经求出了前 k 个方程的解。
这样不容易炸
那么我们存在一个通解
之后我们考虑加入一个元素之后的方程组
之后再让
既然我们已知
众所周知我们需要求的方程是
M x\equiv a_k - ans_{last} \pmod {m_i} 也就是
ax + cy = b 而 exgcd 求解的方程是
ax + by = gcd(a, b) 诶,我们就可以直接用 exgcd 求出
之后考虑 $x\times \frac{a_k - ans_{last}}{gcd(M, m_i)}$ 就可以得到答案 当然 $nowans \text{ 要加上 } M \times x 我们的
M 也要乘上\frac{m_i}{gcd} 原因在开头已经说过了\color{red}\text{重点:}$ 如何判定无解,就是不能 $\gcd(M, m_i) \not| \ a_k - ans_{last} 也可以理解成
(a_k - ans_{last}) 是\gcd(M, m_i) 的倍数
至于为什么要自己写,因为没有题解看得懂
#include <bits/stdc++.h>
using namespace std;
template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for(; isdigit(c);c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= f;
}
#define int long long
const int maxn = 1e5 + 5;
const int maxm = maxn << 1;
typedef int room[maxn];
int exgcd(int a,int b,int &x,int &y) {
if(b == 0) return x = 1, y = 0, a;
int gcd = exgcd(b, a % b, x, y);
int z = x; x = y, y = z - y * (a / b);
return gcd;
}
int ksc(int x,int mi,int mod) {
int ans(0);
while(mi) {
if(mi & 1) ans = (ans + x) % mod;
x = (x + x) % mod;
mi >>= 1;
}
return ans;
}
int n, m[maxn], a[maxn];
int ans, M;
int excrt() {
M = m[1], ans = a[1];
for(int i = 2;i <= n; ++ i) {
int x, y;
int gcd = exgcd(M, m[i], x, y);
int Up = ((a[i] - ans) % m[i] + m[i]) % m[i];
if(Up % gcd) return -1;
x = ksc(x, Up / gcd, m[i]);
// x = (x + m[i]) % m[i];
ans += M * x;
M = M * (m[i] / gcd);
ans = (ans + M) % M;
}
return ans;
}
signed main() {
int i, j;
r1(n);
for(i = 1; i <= n; ++ i) r1(m[i]), r1(a[i]);
printf("%lld\n", excrt());
return 0;
}