「二元一次不定方程(exgcd)」P5656 【模板】二元一次不定方程 (exgcd)

知识点:exgcd

Link:Luogu

为什么之前没写?因为这题出的挺晚,出的时候都忘了嘻嘻

主要抄袭对象:https://www.luogu.com.cn/blog/McHf/p5656-exgcd

简述

T 组数据,每组数据给定参数 a,b,c,描述了一个不定方程 ax+by=c。要求:

  • 如果该方程无整数解,输出 -1
  • 若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 x 的最小值,所有正整数解中 y 的最小值,所有正整数解中 x 的最大值,以及所有正整数解中 y 的最大值。
  • 若方程有整数解,但没有正整数解,你需要输出所有整数解中 x 的最小正整数值,y 的最小正整数值。

1T2×1051a,b,c109
500ms,16MB。

分析

对于 ax+by=c,显然应有 gcd(a,b)c,若不满足该条件则无解,否则由裴蜀定理,ax+by=gcd(a,b) 有无数组整数解,使用 exgcd 即可求得该不定方程的一组整数特解 (x,y),则有:

ax+by=gcd(a,b)ac×xgcd(a,b)+b×c×ygcd(a,b)=c

(c×xgcd(a,b),c×ygcd(a,b)) 即为原不定方程的一组整数特解。我们记这组特解为 (x0,y0),同时记 d=gcd(a,b),接下来考虑在这一组特解的基础上,求得题目所需的解。

考虑将 x0 变为 x0+p,设 p 为正整数,则存在正整数 q,令 x0 变为 x0+p 的同时会将 y0 缩小到 y0q,则有 a(x0+p)+b(y0q)=c。联立 ax0+by0=c 可得 p=b×qa。则有 ab×q,又有 bb×q,则 b×q 的最小值应为 lcm(a,b)。则可得 (x0,y0) 变化的最小变化满足:

{p0=lcm(a,b)a=bdq0=lcm(a,b)a=ad

由于 lcm(a,b)|b×q,易证 (x0,y0) 的变化一定是 (p0,q0) 的整数倍。


然后开始构造答案:

首先将 x0 变为最小的正整数值,即找到 kN,使得:x0+k×p01,则有:k1x0pk 应取 1x0p,则令 x1=x0+k×py1=y0k×q(x1,y1) 即为 x 最小的正整数解。此时检查 y1 的符号,如果 y1>0,则有正整数解,否则由于 x 已经取了最小的正整数解,则 y 不可能也为正整数,原方程无正整数解。

然后考虑每一问:

对于有正整数解的情况:

  • 解的个数。除去当前得到的特解 (x1,y1),其他解的个数即在当前解基础上,保证 y>0 的情况下令 yq0 的次数,答案即 y1q0+1
  • x 的最小整数值。即 x1
  • y 的最小正整数值。即在 y1 基础上减去最多次 q0y 的值,答案即 (y11)modq+1
  • x 的最大整数值。即上一问中 y 对应的 x,答案即 x1+y1q0×p
  • y 的最大整数值。即 y1

对于无正整数解的情况:

  • x 的最小正整数值。即 x1
  • y 的最小正整数值。同构造 x1 时使用的方法,取 k=1y0p,答案即 y0+k×q0

总复杂度 O(TlogA) 级别,A 为值域。


注意烦的一批的类型转换。

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;
}
posted @   Luckyblock  阅读(109)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示