68.Acwing基础课第204题-简单-表达整数的奇怪方式
68.Acwing基础课第204题-简单-表达整数的奇怪方式
题目描述
\(给定 2n 个整数 a_1,a_2,…,a_n 和 m_1,m_2,…,m_n,求一个最小的非负整数 x,满足 ∀i∈[1,n],x≡m_i(mod a_i)\)。
输入格式
\(第一行包含整数 n\)。
\(第 2…n+1 行:第 i+1 行包含两个整数 a_i 和 m_i,数之间用空格隔开\)。
输出格式
输出最小非负整数 x,如果 x 不存在,则输出 −1
数据范围
\(1≤ai≤2^{31}−1\),
\(0≤m_i<a_i\)
\(1≤n≤25\)
\(所有 m_i 的最小公倍数在 64 位有符号整数范围内\)。
输入样例:
2
8 7
11 9
输出样例:
31
代码:
// 包含输入输出流头文件(cin/cout用于输入输出)
#include <iostream>
#include <algorithm>
using namespace std;
// 定义长整型别名:防止大数运算溢出(方程组模数/余数可能很大)
typedef long long LL;
// 扩展欧几里得算法核心函数(适配LL类型)
// 功能:求解 ax + by = gcd(a, b) 的一组整数解 (x, y),并返回gcd(a, b)
// 参数:a,b-方程系数;x,y-引用传递,存储求得的一组解
// 返回值:d = gcd(a, b)(a和b的最大公约数)
LL exgcd(LL a, LL b, LL &x, LL &y)
{
// 递归边界:b=0时,方程为 a*1 + 0*0 = a → 特解x=1, y=0
if (!b)
{
x = 1, y = 0;
return a;
}
// 递归调用:欧几里得核心(gcd(a,b)=gcd(b,a%b)),交换x/y简化后续推导
LL d = exgcd(b, a % b, y, x);
// 回溯推导当前层解:y = y' - (a/b)*x(y'是递归返回的解)
y -= a / b * x;
return d;
}
int main()
{
int n; // n:同余方程组的个数(x ≡ a_i (mod m_i),共n个方程)
cin >> n;
LL x = 0; // 最终满足所有方程的解
LL m1, a1; // 第一个方程:x ≡ a1 (mod m1)
cin >> m1 >> a1;
// 迭代合并后续n-1个方程,逐步求解满足所有方程的解
for (int i = 0; i < n - 1; i ++ )
{
LL m2, a2; // 当前要合并的方程:x ≡ a2 (mod m2)
cin >> m2 >> a2;
LL k1, k2; // 存储扩展欧几里得的解
// 调用exgcd求解:m1*k1 + m2*k2 = gcd(m1, m2)
LL d = exgcd(m1, m2, k1, k2);
// 合并方程的核心判断:
// 现有解满足 x = a1 + k*m1,要满足 x = a2 + t*m2 → a1 + k*m1 = a2 + t*m2
// 整理得:m1*k - m2*t = a2 - a1 → 等价于 m1*k1 + m2*k2 = a2 - a1
// 该方程有解的充要条件:gcd(m1,m2) 能整除 (a2 - a1)
if ((a2 - a1) % d)
{
x = -1; // 无解,标记x=-1并退出循环
break;
}
// ========== 求解合并后的方程特解 ==========
// 1. 扩展欧几里得求得的k1是 m1*k1 + m2*k2 = d 的解,需放大 (a2-a1)/d 倍
k1 *= (a2 - a1) / d;
// 2. 调整k1到最小正整数解:k1的通解为 k1 + k*(m2/d),取模保证解最小且正
LL t = m2 / d;
k1 = (k1 % t + t) % t;
// 3. 计算合并后方程的一个特解:x = k1*m1 + a1(满足前两个方程)
x = k1 * m1 + a1;
// ========== 更新合并后的方程参数 ==========
// 合并后的方程为:x ≡ a1_new (mod m_new)
// m_new = lcm(m1, m2) = |m1*m2| / d(最小公倍数)
LL m = abs(m1 / d * m2);
a1 = k1 * m1 + a1; // 新的余数a1(合并后方程的a)
m1 = m; // 新的模数m1(合并后方程的m)
}
// 若有解,将最终解调整到模m1的最小非负整数解
if (x != -1)
x = (a1 % m1 + m1) % m1;
// 输出结果:-1表示无解,否则为满足所有方程的最小非负解
cout << x << endl;
return 0;
}

浙公网安备 33010602011771号