「二元一次不定方程(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\) 的最小正整数值。

\(1\le T\le 2\times 10^5\)\(1\le a, b, c\le 10^9\)
500ms,16MB。

分析

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

\[\begin{aligned} ax' + by' = \gcd(a, b)\\ a\dfrac{c\times x'}{\gcd(a, b)} + b\times \dfrac{c\times y'}{\gcd(a, b)} = c \end{aligned}\]

\(\left(\dfrac{c\times x'}{\gcd(a, b)},\dfrac{c\times y'}{\gcd(a, b)}\right)\) 即为原不定方程的一组整数特解。我们记这组特解为 \((x_0, y_0)\),同时记 \(d = \gcd(a, b)\),接下来考虑在这一组特解的基础上,求得题目所需的解。

考虑将 \(x_0\) 变为 \(x_0 + p\),设 \(p\) 为正整数,则存在正整数 \(q\),令 \(x_0\) 变为 \(x_0+p\) 的同时会将 \(y_0\) 缩小到 \(y_0-q\),则有 \(a(x_0 + p) + b(y_0 - q) = c\)。联立 \(ax_0+by_0=c\) 可得 \(p = \frac{b\times q}{a}\)。则有 \(a\mid b\times q\),又有 \(b\mid b\times q\),则 \(b\times q\) 的最小值应为 \(\operatorname{lcm} (a,b)\)。则可得 \((x_0, y_0)\) 变化的最小变化满足:

\[\begin{cases} p_0 = \dfrac{\operatorname{lcm}(a,b)}{a} = \dfrac{b}{d}\\ q_0 = \dfrac{\operatorname{lcm}(a,b)}{a} = \dfrac{a}{d} \end{cases}\]

由于 \(\operatorname{lcm} (a,b) | b\times q\),易证 \((x_0, y_0)\) 的变化一定是 \((p_0, q_0)\) 的整数倍。


然后开始构造答案:

首先将 \(x_0\) 变为最小的正整数值,即找到 \(k\in \N\),使得:\(x_0 + k\times p_0 \ge 1\),则有:\(k\ge \frac{1-x_0}{p}\)\(k\) 应取 \(\left\lceil\frac{1-x_0}{p}\right\rceil\),则令 \(x_1 = x_0 + k\times p\)\(y_1 = y_0 - k\times q\)\((x_1, y_1)\) 即为 \(x\) 最小的正整数解。此时检查 \(y_1\) 的符号,如果 \(y_1>0\),则有正整数解,否则由于 \(x\) 已经取了最小的正整数解,则 \(y\) 不可能也为正整数,原方程无正整数解。

然后考虑每一问:

对于有正整数解的情况:

  • 解的个数。除去当前得到的特解 \((x_1, y_1)\),其他解的个数即在当前解基础上,保证 \(y>0\) 的情况下令 \(y-q_0\) 的次数,答案即 \(\left\lfloor\frac{y-1}{q_0}\right\rfloor + 1\)
  • \(x\) 的最小整数值。即 \(x_1\)
  • \(y\) 的最小正整数值。即在 \(y_1\) 基础上减去最多次 \(q_0\)\(y\) 的值,答案即 \((y_1 - 1)\bmod q + 1\)
  • \(x\) 的最大整数值。即上一问中 \(y\) 对应的 \(x\),答案即 \(x_1 + \left\lfloor\frac{y-1}{q_0}\right\rfloor\times p\)
  • \(y\) 的最大整数值。即 \(y_1\)

对于无正整数解的情况:

  • \(x\) 的最小正整数值。即 \(x_1\)
  • \(y\) 的最小正整数值。同构造 \(x_1\) 时使用的方法,取 \(k = \left\lceil\frac{1-y_0}{p}\right\rceil\),答案即 \(y_0 + k\times q_0\)

总复杂度 \(O(T\log A)\) 级别,\(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 @ 2023-02-12 10:10  Luckyblock  阅读(74)  评论(0编辑  收藏  举报