{ }codelper
최대공약수여러 값 돌려주기P0181

버튼이 두 개뿐인 승강기

위아래로 끝없이 이어진 건물에 버튼이 두 개뿐인 승강기가 있다. ▲ 버튼을 한 번 누르면 정확히 a층 올라가고, ▼ 버튼을 한 번 누르면 정확히 b층 내려간다. 승강기는 0층에서 출발하고, t층에 서고 싶다(t가 음수이면 지하다). ▲를 x번, ▼를 y번 누르면 도착하는 층은 x×a − y×b이고, 누르는 순서는 상관없다. x×a − y×b = t를 만족하는 0 이상의 정수 x, y 가운데 x + y가 가장 작은 것을 찾아 "x y"를 출력하라. 그런 x, y가 없으면 -1을 출력한다. 가장 작은 x + y를 내는 (x, y)는 늘 하나뿐이다. 질문 q개에 차례로 한 줄씩 답한다.

입력
첫 줄에 q, 다음 q줄에 a b t.
1 ≤ q ≤ 5,000, 1 ≤ a, b ≤ 1,000,000, −1,000,000,000 ≤ t ≤ 1,000,000,000
출력
질문마다 한 줄에 "x y" 또는 -1
예시 1 · stdin
3
5 3 7
4 6 3
2 2 0
stdout
2 1
-1
0 0
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

x×a − y×b = t는 g = gcd(a, b)가 t를 나눌 때만 풀려요.
확장 유클리드 호제법이 a×s + b×r = g인 s를 함께 주면, x는 s×(t/g)를 b/g로 나눈 나머지에서 시작해 b/g씩만 바뀌어요.
여기에 y ≥ 0(즉 x×a ≥ t)이 되도록 x를 b/g씩 올리면 x와 y가 함께 가장 작아져요.
확장 유클리드는 값 세 개(g, s, r)를 돌려줘야 해서 언어마다 모양이 달라요: Python은 튜플 return g, y, x - (a // b) * y, JavaScript는 배열 [g, x, y]과 구조 분해, TypeScript는 튜플 타입 [number, number, number], Java는 record Ext(long g, long x, long y), C#은 이름 붙은 튜플 (long g, long x, long y), C++는 std::tuple과 auto [g, x, y], C는 값 하나만 돌려주니 long long *x, long long *y 포인터로 채워 줘요.
크레이콘은 목록 [g, x, y]를 돌려주고 r[0]처럼 꺼내요.
함정 ① 나머지: s, t, x − low는 음수가 될 수 있는데 Python 말고는 모두(JavaScript·TypeScript·Java·C#·C·C++·크레이콘) %가 음수를 남겨요.
특히 x를 low 위로 올리는 low + (x - low) % step에서 나머지가 음수이면 x가 low보다 작아져 y가 음수가 돼요 — ((v % m) + m) % m이나 Java Math.floorMod를 써요.
② 넘침: s를 줄인 값과 t/g를 줄인 값의 곱은 10¹²까지 가서 Java·C#·C·C++의 int(약 21억)를 넘어요 — long/long long을 써요.
JavaScript·크레이콘의 수는 2⁵³까지 정확해서 이 범위는 괜찮아요.
③ 가장 작은 x를 그대로 쓰면 y가 음수가 될 수 있어요.
y ≥ 0이 될 때까지 x를 b/g씩 올려야 해요.

Pythonmain.py · 35 줄
import sys  def ext_gcd(a, b):    """a*x + b*y == g 인 (g, x, y)를 한꺼번에 돌려준다."""    if b == 0:        return a, 1, 0    g, x, y = ext_gcd(b, a % b)    return g, y, x - (a // b) * y  def presses(a, b, t):    """올라가기 횟수 x, 내려가기 횟수 y (x*a - y*b == t, x+y가 가장 작게). 안 되면 None."""    g, s, _ = ext_gcd(a, b)    if t % g != 0:        return None    step = b // g  # x는 이 간격으로만 바꿀 수 있다    x = (s % step) * ((t // g) % step) % step    low = max(0, -(-t // a))  # y >= 0 이려면 x*a >= t    x = low + (x - low) % step  # low 이상에서 x와 나머지가 같은 가장 작은 수    return x, (x * a - t) // b  def main():    data = sys.stdin.read().split()    q = int(data[0])    out = []    for i in range(q):        a, b, t = map(int, data[1 + 3 * i: 4 + 3 * i])        r = presses(a, b, t)        out.append("-1" if r is None else f"{r[0]} {r[1]}")    print("\n".join(out))  main()
JavaScriptmain.js · 31 줄
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // a*x + b*y == g 인 [g, x, y]를 배열 하나로 돌려준다function extGcd(a, b) {  if (b === 0) return [a, 1, 0];  const [g, x, y] = extGcd(b, a % b);  return [g, y, x - Math.floor(a / b) * y];} // 수학의 나머지(늘 0 이상). JS의 %는 음수에서 음수가 나온다const mod = (v, m) => ((v % m) + m) % m; // [올라가기 횟수, 내려가기 횟수], 안 되면 nullfunction presses(a, b, t) {  const [g, s] = extGcd(a, b);  if (t % g !== 0) return null;  const step = b / g;  let x = (mod(s, step) * mod(t / g, step)) % step;  const low = Math.max(0, Math.ceil(t / a)); // y >= 0 이려면 x*a >= t  x = low + mod(x - low, step); // low 이상에서 x와 나머지가 같은 가장 작은 수  return [x, (x * a - t) / b];} const q = data[0];const out = [];for (let i = 0; i < q; i++) {  const [a, b, t] = data.slice(1 + 3 * i, 4 + 3 * i);  const r = presses(a, b, t);  out.push(r === null ? "-1" : `${r[0]} ${r[1]}`);}console.log(out.join("\n"));
TypeScriptmain.ts · 32 줄
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // a*x + b*y == g 인 [g, x, y]를 튜플 하나로 돌려준다function extGcd(a: number, b: number): [number, number, number] {  if (b === 0) return [a, 1, 0];  const [g, x, y] = extGcd(b, a % b);  return [g, y, x - Math.floor(a / b) * y];} // 수학의 나머지(늘 0 이상). %는 음수에서 음수가 나온다const mod = (v: number, m: number): number => ((v % m) + m) % m; // [올라가기 횟수, 내려가기 횟수], 안 되면 nullfunction presses(a: number, b: number, t: number): [number, number] | null {  const [g, s] = extGcd(a, b);  if (t % g !== 0) return null;  const step = b / g;  let x = (mod(s, step) * mod(t / g, step)) % step;  const low = Math.max(0, Math.ceil(t / a)); // y >= 0 이려면 x*a >= t  x = low + mod(x - low, step); // low 이상에서 x와 나머지가 같은 가장 작은 수  return [x, (x * a - t) / b];} const q = data[0];const out: string[] = [];for (let i = 0; i < q; i++) {  const [a, b, t] = data.slice(1 + 3 * i, 4 + 3 * i);  const r = presses(a, b, t);  out.push(r === null ? "-1" : `${r[0]} ${r[1]}`);}console.log(out.join("\n"));
JavaMain.java · 41 줄
import java.io.*;import java.util.*; public class Main {    // a*x + b*y == g 인 (g, x, y). Java 메서드는 값 하나만 돌려주니 record로 묶는다    record Ext(long g, long x, long y) {}     static Ext extGcd(long a, long b) {        if (b == 0) return new Ext(a, 1, 0);        Ext e = extGcd(b, a % b);        return new Ext(e.g(), e.y(), e.x() - (a / b) * e.y());    }     // 수학의 나머지(늘 0 이상). Java의 %는 음수에서 음수가 나온다    static long mod(long v, long m) {        return Math.floorMod(v, m);    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int i = 0; i < q; i++) {            StringTokenizer st = new StringTokenizer(in.readLine());            long a = Long.parseLong(st.nextToken());            long b = Long.parseLong(st.nextToken());            long t = Long.parseLong(st.nextToken());            Ext e = extGcd(a, b);            if (t % e.g() != 0) {                out.append("-1\n");                continue;            }            long step = b / e.g();            long x = mod(e.x(), step) * mod(t / e.g(), step) % step;            long low = Math.max(0, Math.floorDiv(t + a - 1, a)); // y >= 0 이려면 x*a >= t            x = low + mod(x - low, step); // low 이상에서 x와 나머지가 같은 가장 작은 수            out.append(x).append(' ').append((x * a - t) / b).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 39 줄
using System;using System.Text; class Program{    // a*x + b*y == g 인 (g, x, y)를 튜플로 한꺼번에 돌려준다    static (long g, long x, long y) ExtGcd(long a, long b)    {        if (b == 0) return (a, 1, 0);        var (g, x, y) = ExtGcd(b, a % b);        return (g, y, x - (a / b) * y);    }     // 수학의 나머지(늘 0 이상). C#의 %는 음수에서 음수가 나온다    static long Mod(long v, long m) => ((v % m) + m) % m;     static void Main()    {        int q = int.Parse(Console.ReadLine().Trim());        var out_ = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            long a = long.Parse(p[0]), b = long.Parse(p[1]), t = long.Parse(p[2]);            var (g, s, _) = ExtGcd(a, b);            if (t % g != 0)            {                out_.Append("-1\n");                continue;            }            long step = b / g;            long x = Mod(s, step) * Mod(t / g, step) % step;            long low = t > 0 ? (t + a - 1) / a : 0; // y >= 0 이려면 x*a >= t            x = low + Mod(x - low, step); // low 이상에서 x와 나머지가 같은 가장 작은 수            out_.Append(x).Append(' ').Append((x * a - t) / b).Append('\n');        }        Console.Write(out_);    }}
C++main.cpp · 34 줄
#include <iostream>#include <tuple> // a*x + b*y == g 인 (g, x, y)를 tuple로 한꺼번에 돌려준다std::tuple<long long, long long, long long> extGcd(long long a, long long b) {    if (b == 0) return {a, 1, 0};    auto [g, x, y] = extGcd(b, a % b);    return {g, y, x - (a / b) * y};} // 수학의 나머지(늘 0 이상). C++의 %는 음수에서 음수가 나온다long long mod(long long v, long long m) { return (v % m + m) % m; } int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int q;    std::cin >> q;    while (q--) {        long long a, b, t;        std::cin >> a >> b >> t;        auto [g, s, r] = extGcd(a, b);        (void)r;        if (t % g != 0) {            std::cout << "-1\n";            continue;        }        long long step = b / g;        long long x = mod(s, step) * mod(t / g, step) % step;        long long low = t > 0 ? (t + a - 1) / a : 0;  // y >= 0 이려면 x*a >= t        x = low + mod(x - low, step);  // low 이상에서 x와 나머지가 같은 가장 작은 수        std::cout << x << ' ' << (x * a - t) / b << '\n';    }}
Cmain.c · 40 줄
#include <stdio.h> /* a*x + b*y == g. C 함수는 값 하나만 돌려주니 x, y는 포인터로 받아 채운다 */long long ext_gcd(long long a, long long b, long long *x, long long *y) {    if (b == 0) {        *x = 1;        *y = 0;        return a;    }    long long x1, y1;    long long g = ext_gcd(b, a % b, &x1, &y1);    *x = y1;    *y = x1 - (a / b) * y1;    return g;} /* 수학의 나머지(늘 0 이상). C의 %는 음수에서 음수가 나온다 */long long mod(long long v, long long m) {    return (v % m + m) % m;} int main(void) {    int q;    scanf("%d", &q);    while (q--) {        long long a, b, t, s, r;        scanf("%lld %lld %lld", &a, &b, &t);        long long g = ext_gcd(a, b, &s, &r);        if (t % g != 0) {            puts("-1");            continue;        }        long long step = b / g;        long long x = mod(s, step) * mod(t / g, step) % step;        long long low = t > 0 ? (t + a - 1) / a : 0; /* y >= 0 이려면 x*a >= t */        x = low + mod(x - low, step); /* low 이상에서 x와 나머지가 같은 가장 작은 수 */        printf("%lld %lld\n", x, (x * a - t) / b);    }    return 0;}
Craykonmain.cray · 41 줄
// a*x + b*y == g 인 [g, x, y]를 목록 하나로 돌려준다확장유클리드(a, b){    만약 (b == 0)    {        돌려주기 [a, 1, 0];    }    r = 확장유클리드(b, a % b);    돌려주기 [r[0], r[2], r[1] - 수학.몫(a, b) * r[2]];} // 수학의 나머지(늘 0 이상). %는 음수에서 음수가 나온다나머지(v, m){    돌려주기 (v % m + m) % m;} q = 콘솔.줄읽기().수로변환();out = "";반복 (i = 0; i < q; i++){    abt = 콘솔.수들읽기();    a = abt[0];    b = abt[1];    t = abt[2];    e = 확장유클리드(a, b);    g = e[0];    만약 (t % g != 0)    {        out += "-1\n";    }    아니면    {        step = 수학.몫(b, g);        x = 나머지(e[1], step) * 나머지(수학.몫(t, g), step) % step;        low = 수학.큰수(0, 수학.올림(t / a)); // y >= 0 이려면 x*a >= t        x = low + 나머지(x - low, step); // low 이상에서 x와 나머지가 같은 가장 작은 수        out += x + " " + 수학.몫(x * a - t, b) + "\n";    }}콘솔.이어쓰기(out);