67.Acwing基础课第878题-简单-线性同余方程
67.Acwing基础课第878题-简单-线性同余方程
题目描述
\(给定 n 组数据 a_i,b_i,m_i,对于每组数求出一个 x_i,使其满足 a_i×x_i≡b_i(mod\ m_i),如果无解则输出 impossible\)。
输入格式
\(第一行包含整数 n\)。
\(接下来 n 行,每行包含三个整数 a_i,p_i,m_i\)。
输出格式
输出共 n 行,每组数据输出一个整数表示一个满足条件的 \(x_i\),如果无解则输出 impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在 int 范围之内。
数据范围
\(1≤n≤10^5\),
\(1≤a_i,b_i,m_i≤2×10^9\)
输入样例:
2
2 3 6
4 3 5
输出样例:
impossible
-3
代码:
// 包含输入输出流头文件(cin/cout依赖,本代码用C风格IO,更适合竞赛大数据量)
#include <iostream>
#include <algorithm>
using namespace std;
// 定义长整型别名:防止乘法/取模过程中溢出int范围
typedef long long LL;
// 扩展欧几里得算法核心函数
// 功能:求解 ax + my = gcd(a, m) 的一组整数解 (x, y),并返回gcd(a, m)
// 参数说明:
// a, m:方程的系数(对应线性同余方程的 a 和模数 m)
// x, y:引用传递,存储求得的一组解
// 返回值:d = gcd(a, m)(a和m的最大公约数)
int exgcd(int a, int b, int &x, int &y)
{
// 递归边界:当b=0时,方程变为 a*1 + 0*0 = a,即gcd(a,0)=a
if (!b)
{
x = 1, y = 0; // 此时特解为x=1,y=0
return a; // 返回最大公约数a
}
// 递归调用:欧几里得算法核心(gcd(a,b)=gcd(b,a%b))
// 注意:递归时交换x和y的位置,简化后续解的推导
int d = exgcd(b, a % b, y, x);
// 回溯推导当前层的解:
// 推导依据:b*x' + (a%b)*y' = d → 整理得 a*y' + b*(x' - (a/b)*y') = d
// 因递归交换了x/y,当前x=y',y=x',故简化为 y -= a/b * x
y -= a / b * x;
return d; // 返回a和b的最大公约数
}
int main()
{
int n; // n:测试用例的组数
// C风格输入:比cin更快,适合竞赛场景
scanf("%d", &n);
// 循环处理n组测试用例(每组求解线性同余方程 a*x ≡ b (mod m))
while (n -- )
{
int a, b, m;
// a:线性同余方程的系数;b:余数;m:模数
// 目标:求解 a*x ≡ b (mod m) 的一个整数解x
scanf("%d%d%d", &a, &b, &m);
int x, y; // 存储扩展欧几里得算法的解
// 调用exgcd求解 a*x + m*y = gcd(a, m)
int d = exgcd(a, m, x, y);
// 线性同余方程有解的充要条件:gcd(a,m) 能整除 b(即 b % d == 0)
if (b % d)
puts("impossible"); // 无解,输出impossible
else
{
// 有解时,先求原方程 a*x = b 的一个特解,再调整为模m的解:
// 1. 扩展欧几里得求得的x是 a*x + m*y = d 的解 → 两边乘 b/d 得 a*(x*b/d) + m*(y*b/d) = b
// 2. 因此 x0 = x * (b/d) 是 a*x ≡ b (mod m) 的一个特解
// 3. 对m取模,保证解在合理范围内;(LL)强制转换防止溢出
printf("%d\n", (LL)b / d * x % m);
}
}
return 0; // 程序正常结束
}

浙公网安备 33010602011771号