扩展中国剩余定理证明及例题 Strange Way to Express Integers

前置知识

中国剩余定理(CRT),逆元;

EXCRT是什么

我们知道

对于

\[\begin{equation} \begin{cases} x \equiv c_1 \ (mod \ m_1) \\ x \equiv c_2 \ (mod \ m_2) \\ .\\ .\\ .\\ x \equiv c_i \ (mod \ \ m_i) \\ \end{cases} \end{equation} \]

一个一元线性同余方程组,GCT适用于模数是质数的情况,如果模数不是质数,就要用到EXGCT了;

EXCRT证明及用法

证明

首先,联立前两个式子,得

\[\begin{equation} \begin{cases} x \equiv c_1 \ (mod \ m_1) \\ x \equiv c_2 \ (mod \ m_2) \\ \end{cases} \end{equation} \]

进而

\[x = c_1 + m_1k_1 = c_2 + m_2k_2 \]

\[c_1 + m_1k_1 = c_2 + m_2k_2 \]

\[m_1k_1 = c_2 - c_1 + m_2k_2 \]

等式两边同除以$ gcd(m_1, m_2) $, 得:

\[\frac{m_1}{gcd(m_1, m_2)}k_1 = \frac{c_2 - c_1}{gcd(m_1, m_2)} + \frac{m_2}{gcd(m_1, m_2)}k_2 \]

\[\frac{m_1}{gcd(m_1, m_2)}k_1 \equiv \frac{c_2 - c_1}{gcd(m_1, m_2)} \ (mod \ \frac{m_2}{gcd(m_1, m_2)}) \]

到这里,我们就把 \(k_2\) 消掉了;

根据同余式的同乘性,同余式两边同除 $ \frac{m_1}{gcd(m_1, m_2)} $,得( $ x^{-1} $ 代表 $ x $ 在模意义下的逆元):

\[k_1 \equiv \frac{c_2 - c_1}{gcd(m_1, m_2)} \ * \ (\frac{m_1}{gcd(m_1, m_2)})^{-1} \ (mod \ \frac{m_2}{gcd(m_1, m_2)}) \]

\[k_1 = \frac{c_2 - c_1}{gcd(m_1, m_2)} \ * \ (\frac{m_1}{gcd(m_1, m_2)})^{-1} \ + \ y \ * \ \frac{m_2}{gcd(m_1, m_2)} \]

$ y $ 是整数;

将 $ k_1 $ 带回 $ x = c_1 + m_1k_1 $ 中,得:

注意,下面的 $ (\frac{m_1}{gcd(m_1, m_2)})^{-1} $ 指的都是其在mod\(\frac{m_2}{gcd(m_1, m_2)}\) 下的逆元!

\[x = m_1\frac{c_2 - c_1}{gcd(m_1, m_2)} \ * \ (\frac{m_1}{gcd(m_1, m_2)})^{-1} \ + \ c_1 \ + \ y \ * \ \frac{m_1m_2}{gcd(m_1, m_2)} \]

\[x \equiv m_1\frac{c_2 - c_1}{gcd(m_1, m_2)} \ * \ (\frac{m_1}{gcd(m_1, m_2)})^{-1} \ + \ c_1 \ (mod \ \frac{m_1m_2}{gcd(m_1, m_2)}) \]

其中,$ \frac{m_1m_2}{gcd(m_1, m_2)} = lcm(m1, m2)$;

到这,我们可以将最后一个式子与 $ x \equiv c_2 \ (mod \ m_2) $ 联立,以此类推,进行递归求解;

无解情况

显然,式子中的每个系数都应是整数,所以 $ c_2 - c_1 $ 应该能整除 $ gcd(m_1, m_2) $,若不能整除,则无解;

例题

Strange Way to Express Integers

板子;

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
__int128 read() {
    __int128 x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

void out(__int128 x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) out(x / 10);
    putchar(x % 10 + '0');
}
long long n;
__int128 m[1000005], c[1000005];
__int128 gcd(__int128 a, __int128 b) {
	if (b == 0) return a;
	else return gcd(b, a % b);
}
__int128 phi(long long nn) {
	__int128 mm = sqrt(nn);
	__int128 ans = nn;
	for (__int128 i = 2; i <= mm; i++) {
		if (nn % i == 0) {
			ans = ans / i * (i - 1);
			while(nn % i == 0) nn /= i;
		}
	}
	if (nn > 1) ans = ans / nn * (nn - 1);
	return ans;
}
__int128 qpow(__int128 a, __int128 b, __int128 p) {
	__int128 ans = 1;
	while(b) {
		if (b & 1) ans = ans * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return ans;
}
inline __int128 inv(__int128 a, long long b) {
	__int128 pb = phi(b);
	return qpow(a, pb - 1, b);
}
int main() {
	while(scanf("%lld", &n) != EOF) {
		bool v = true;
		for (int i = 1; i <= n; i++) {
			m[i] = read();
			c[i] = read();
		}
		for (int i = 2; i <= n; i++) {
			__int128 m1 = m[i - 1], m2 = m[i], c1 = c[i - 1], c2 = c[i];
			__int128 g = gcd(m1, m2);
			if ((c2 - c1) % g != 0) {
				printf("%d\n", -1);
				v = false;
				break;
			}
			m[i] = (m1 * m2) / g;
			c[i] = inv(m1 / g, (long long)m2 / g) * (c2 - c1) / g * m1 + c1;
			c[i] = (c[i] % m[i] + m[i]) % m[i];
		}
		if (!v) continue;
		out(c[n]); //最后c[n]为答案,且c[n]是答案中最小的,因为其已经mod了所有m[i];
		printf("\n");
	}
	return 0;
}
posted @ 2024-04-16 09:54  Peppa_Even_Pig  阅读(11)  评论(0编辑  收藏  举报