mobile wallpaper 1
mobile wallpaper 2
mobile wallpaper 3
mobile wallpaper 4
641 字
2 分钟
线性同余方程与中国剩余定理
2026-03-31

线性同余方程与中国剩余定理#

线性同余方程#

问题形式: 给定 a、b、m,求整数 x 使得 a·x ≡ b (mod m)。它等价于存在整数 y 满足 a·x - m·y = b,即一个二元一次不定方程。

有解条件: 由裴蜀定理(Bézout),ax + my 能取到的值恰好是 gcd(a, m) 的所有倍数,所以方程有解 当且仅当 b 是 gcd(a, m) 的倍数

求解步骤:

  1. 用扩展欧几里得求出 ax’ + my’ = gcd(a, m) 的一组特解 x’;
  2. 把特解放大 b / g 倍,得到原方程的一个特解 x0 = x’ · (b / g);
  3. 通解为 x = x0 + k · (m / g)(k 为任意整数),因此把 x0 对 m/g 取模(取正)即可得到最小非负解。

例题#

题意#

给定 nn 组数据 ai,bi,mia_i, b_i, m_i,对于每组数求出一个 xix_i,使其满足 aixibi(modmi).a_i x_i \equiv b_i \pmod{m_i}. 若无解,则输出 impossible

输入格式#

第一行一个整数 nn
接下来 nn 行,每行三个整数 ai,bi,mia_i, b_i, m_i

输出格式#

nn 行,每行输出一个满足条件的 xix_i(若存在);若无解则输出 impossible

数据范围#

1n105,1ai,bi,mi2×1091 \leq n \leq 10^5,\quad 1 \leq a_i, b_i, m_i \leq 2 \times 10^9

import java.io.*;
import java.util.StringTokenizer;
public class Main {
/* 扩展欧几里得:求 ax + by = gcd(a,b),返回 gcd */
static long exgcd(long a, long b, long[] xy) {
if (b == 0) { xy[0] = 1; xy[1] = 0; return a; }
long g = exgcd(b, a % b, xy);
long x = xy[0], y = xy[1];
xy[0] = y;
xy[1] = x - (a / b) * y;
return g;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
int n = Integer.parseInt(br.readLine().trim());
while (n-- > 0) {
StringTokenizer st = new StringTokenizer(br.readLine());
long a = Long.parseLong(st.nextToken());
long b = Long.parseLong(st.nextToken());
long m = Long.parseLong(st.nextToken());
long[] xy = new long[2];
long g = exgcd(a, m, xy); // a*x' + m*y' = g
if (b % g != 0) { // 裴蜀定理:b 必须是 g 的倍数才有解
pw.println("impossible");
} else {
long x = xy[0] * (b / g) % m; // 放大 b/g 倍得到特解
x = (x % m + m) % m; // 取最小非负解
pw.println(x);
}
}
pw.flush();
}
}

中国剩余定理#

解决的问题: 求解同余方程组

x ≡ m₁ (mod a₁),x ≡ m₂ (mod a₂),…,x ≡ mₙ (mod aₙ)

其中各模数 aᵢ 不一定互质(互质时用经典的 CRT 公式;不互质时用下面的”两两合并法”,它是通法)。

两两合并法: 假设已经合并出前 k 个方程的解:x ≡ m₁ (mod a₁),即 x = a₁·t + m₁。代入下一个方程 x ≡ m₂ (mod a₂),得到 a₁·t ≡ m₂ - m₁ (mod a₂)——这就是一个线性同余方程。用扩展欧几里得解出 t(若 m₂ - m₁ 不是 gcd(a₁, a₂) 的倍数则整个方程组无解),回代得到新的 m₁’ = a₁·t + m₁,新的模数 a₁’ = lcm(a₁, a₂) = a₁/g·a₂。重复 n-1 次即可。最终答案就是最后留下的 m₁(取最小非负)。

表达整数的奇怪方式#

题目描述#

给定 2n2n 个整数 a1,a2,,ana_1, a_2, \dots, a_nm1,m2,,mnm_1, m_2, \dots, m_n,求一个最小非负整数 xx,满足
i[1,n],xmi(modai).\forall i \in [1, n],\quad x \equiv m_i \pmod{a_i}.
若不存在这样的 xx,输出 1-1

输入格式#

11 行:整数 nn
22n+1n+1 行:每行两个整数 ai,mia_i, m_i,用空格隔开。

输出格式#

输出最小非负整数 xx;若不存在,输出 1-1

数据范围#

1n2×105,1ai2311,0mi<ai1 \leq n \leq 2 \times 10^5,\quad 1 \leq a_i \leq 2^{31} - 1,\quad 0 \leq m_i < a_i

import java.io.*;
import java.util.StringTokenizer;
public class Main {
/* 扩展欧几里得:ax + by = gcd(a,b),返回 gcd */
static long exgcd(long a, long b, long[] xy) {
if (b == 0) { xy[0] = 1; xy[1] = 0; return a; }
long g = exgcd(b, a % b, xy);
long x = xy[0], y = xy[1];
xy[0] = y;
xy[1] = x - (a / b) * y;
return g;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
boolean ok = true;
// 读入第一个方程 x ≡ m1 (mod a1)
StringTokenizer st = new StringTokenizer(br.readLine());
long a1 = Long.parseLong(st.nextToken());
long m1 = Long.parseLong(st.nextToken());
// 依次把后面的方程合并进来
for (int i = 1; i < n; i++) {
st = new StringTokenizer(br.readLine());
long a2 = Long.parseLong(st.nextToken());
long m2 = Long.parseLong(st.nextToken());
// 解 a1 * t ≡ m2 - m1 (mod a2)
long[] xy = new long[2];
long g = exgcd(a1, a2, xy);
if ((m2 - m1) % g != 0) { ok = false; break; } // 无解
long k1 = xy[0] * ((m2 - m1) / g); // 特解
long mod = a2 / g;
k1 = (k1 % mod + mod) % mod; // t 的最小非负解
m1 = m1 + a1 * k1; // 合并后的余数
a1 = a1 / g * a2; // 合并后的模数 = lcm(a1, a2)
m1 = (m1 % a1 + a1) % a1; // 取最小非负
}
System.out.println(ok ? m1 : -1);
}
}
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

线性同余方程与中国剩余定理
https://blogstella.xyz/posts/数据结构之线性同余方程与中国剩余定理/
作者
Stella
发布于
2026-03-31
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录