「二元一次不定方程(exgcd)」P5656 【模板】二元一次不定方程 (exgcd)
知识点:exgcd
Link:Luogu
为什么之前没写?因为这题出的挺晚,出的时候都忘了嘻嘻
主要抄袭对象:https://www.luogu.com.cn/blog/McHf/p5656-exgcd。
简述
组数据,每组数据给定参数 ,描述了一个不定方程 。要求:
- 如果该方程无整数解,输出
-1
。- 若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 的最小值,所有正整数解中 的最小值,所有正整数解中 的最大值,以及所有正整数解中 的最大值。
- 若方程有整数解,但没有正整数解,你需要输出所有整数解中 的最小正整数值, 的最小正整数值。
,。
500ms,16MB。
分析
对于 ,显然应有 ,若不满足该条件则无解,否则由裴蜀定理, 有无数组整数解,使用 exgcd 即可求得该不定方程的一组整数特解 ,则有:
即为原不定方程的一组整数特解。我们记这组特解为 ,同时记 ,接下来考虑在这一组特解的基础上,求得题目所需的解。
考虑将 变为 ,设 为正整数,则存在正整数 ,令 变为 的同时会将 缩小到 ,则有 。联立 可得 。则有 ,又有 ,则 的最小值应为 。则可得 变化的最小变化满足:
由于 ,易证 的变化一定是 的整数倍。
然后开始构造答案:
首先将 变为最小的正整数值,即找到 ,使得:,则有:, 应取 ,则令 ,, 即为 最小的正整数解。此时检查 的符号,如果 ,则有正整数解,否则由于 已经取了最小的正整数解,则 不可能也为正整数,原方程无正整数解。
然后考虑每一问:
对于有正整数解的情况:
- 解的个数。除去当前得到的特解 ,其他解的个数即在当前解基础上,保证 的情况下令 的次数,答案即 。
- 的最小整数值。即 。
- 的最小正整数值。即在 基础上减去最多次 后 的值,答案即 。
- 的最大整数值。即上一问中 对应的 ,答案即 。
- 的最大整数值。即 。
对于无正整数解的情况:
- 的最小正整数值。即 。
- 的最小正整数值。同构造 时使用的方法,取 ,答案即 。
总复杂度 级别, 为值域。
注意烦的一批的类型转换。
ceil()
的返回值是 double
类型,long long * double = double
,此时可能会出现精度损失,需要注意作乘法前先把 ceil()
的返回值转化为 long long
类型。
代码
复制复制//By:Luckyblock /* */ #include <cmath> #include <cstdio> #include <cctype> #include <algorithm> #define LL long long //============================================================= //============================================================= inline LL read() { LL f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1; for (; isdigit(ch); ch = getchar()) w = (w << 3ll) + (w << 1ll) + ch - '0'; return f * w; } LL exgcd(LL a_, LL b_, LL &x_, LL &y_) { if (!b_) { x_ = 1, y_ = 0; return a_; } LL d_ = exgcd(b_, a_ % b_, y_, x_); y_ -= a_ / b_ * x_; return d_; } //============================================================= int main() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { LL a = read(), b = read(), c = read(), x, y; LL d = exgcd(a, b, x, y); if (c % d != 0) { printf("-1\n"); continue; } x *= c / d, y *= c / d; LL p = b / d, q = a / d, k; k = ceil((1.0 - x) / p), x += p * k, y -= q * k; if (y > 0) { printf("%lld ", (y - 1) / q + 1); printf("%lld ", x); printf("%lld ", (y - 1) % q + 1); printf("%lld ", x + (y - 1) / q * p); printf("%lld ", y); } else { printf("%lld ", x); printf("%lld ", y + q * (LL) ceil((1.0 - y) / q)); } printf("\n"); } return 0; }
作者@Luckyblock,转载请声明出处。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战