{ }codelper
最大公约数P0313

圆桌上每隔 k 个座位放的糖

圆桌周围摆着 n 把椅子,按顺时针方向从 0 到 n−1 编号。
从 n−1 号椅子顺时针走一格就是 0 号椅子。
先在 0 号椅子上放一颗糖。
之后,每次从刚放过糖的椅子顺时针走 k 格,在到达的椅子上放一颗糖。
如果到达的椅子上已经有糖,就不放并停下。
设 n 和 k 的最大公约数为 g,已知停下时放有糖的椅子恰好是编号为 g 的倍数的全部椅子(0 也算 g 的倍数)。
一行给出 n 和 k,用一个空格分开。
按顺序输出放有糖的椅子数,以及放有糖的椅子的编号中最大的一个,用一个空格分开,占一行。

输入
1 ≤ n ≤ 1,000,000,000,1 ≤ k ≤ 1,000,000,000(整数,只给出数字,没有逗号)。
k 可能比 n 大
输出
一行,放有糖的椅子数和其中最大的编号,用一个空格分开
样例 1 · stdin
12 8
stdout
3 8
样例 2 · stdin
5 10
stdout
1 0
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

有的语言有求最大公约数的函数,有的没有。
Python 直接用 math.gcd(n, k),C++ 用 <numeric> 里的 std::gcd(n, k)(C++17 起)。
JS、TS、C、Craykon 没有;Java 和 C# 只有大数类型的(BigInteger.gcd、BigInteger.GreatestCommonDivisor),所以对 int 通常自己写。
写法是辗转相除法(欧几里得算法)。
不断把 (a, b) 换成 (b, a % b),直到 b 为 0,剩下的 a 就是最大公约数。
第一个陷阱是替换的顺序。
写成 a = b; b = a % b; 时 a 已经变成了 b,于是算的是 b % b = 0,最大公约数总是得到 k。
样例 1 变成 1 4。
先把余数存进临时变量,或者像 Python a, b = b, a % b、JS/TS [a, b] = [b, a % b]、C# (a, b) = (b, a % b) 那样同时替换。
第二个陷阱是用反复相减代替取余。
对 1000000000 1 或 1 1000000000 这样的输入要减大约 10 亿次,无法在时限内结束。
用取余的话,这个范围内的任何输入几十次以内就结束。
第三个陷阱是不求最大公约数,直接用 k 计算。
样例 1 会得到 12 除以 8 的商 1 和 12 − 8 = 4,但答案是 3 8。
第四个陷阱是 Python 的 /。
n / g 即使整除也是浮点数,样例 1 会输出 3.0 8。
求商要用 n // g。
JS、TS 和 Craykon 的 / 也会除到小数,但值是整数时输出 3。
在 Craykon 中用 数学.商(n, g) 求商。

Pythonmain.py · 6 行
import math n, k = map(int, input().split())g = math.gcd(n, k)# //는 몫(정수)이다. /를 쓰면 3.0처럼 실수가 된다print(n // g, n - g)
JavaScriptmain.js · 9 行
const [n, k] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);// 유클리드 호제법: b가 0이 될 때까지 (a, b)를 (b, a % b)로 바꾼다let a = n;let b = k;while (b !== 0) {  [a, b] = [b, a % b];}const g = a;console.log(`${n / g} ${n - g}`);
TypeScriptmain.ts · 10 行
import { readFileSync } from "node:fs";const [n, k]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);// 유클리드 호제법: b가 0이 될 때까지 (a, b)를 (b, a % b)로 바꾼다let a: number = n;let b: number = k;while (b !== 0) {  [a, b] = [b, a % b];}const g: number = a;console.log(`${n / g} ${n - g}`);
JavaMain.java · 19 行
import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int n = sc.nextInt();        int k = sc.nextInt();        // 유클리드 호제법: b가 0이 될 때까지 (a, b)를 (b, a % b)로 바꾼다        int a = n;        int b = k;        while (b != 0) {            int r = a % b;   // 나머지를 먼저 담아 둔다            a = b;            b = r;        }        int g = a;        System.out.println((n / g) + " " + (n - g));    }}
C#Main.cs · 20 行
using System; class Program{    static void Main()    {        string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(p[0]);        int k = int.Parse(p[1]);        // 유클리드 호제법: b가 0이 될 때까지 (a, b)를 (b, a % b)로 바꾼다        int a = n;        int b = k;        while (b != 0)        {            (a, b) = (b, a % b);        }        int g = a;        Console.WriteLine($"{n / g} {n - g}");    }}
C++main.cpp · 9 行
#include <iostream>#include <numeric> int main() {    int n, k;    std::cin >> n >> k;    int g = std::gcd(n, k);   // <numeric>, C++17부터    std::cout << n / g << " " << n - g << "\n";}
Cmain.c · 16 行
#include <stdio.h> int main(void) {    int n, k;    scanf("%d %d", &n, &k);    /* 유클리드 호제법: b가 0이 될 때까지 (a, b)를 (b, a % b)로 바꾼다 */    int a = n, b = k;    while (b != 0) {        int r = a % b;   /* 나머지를 먼저 담아 둔다 */        a = b;        b = r;    }    int g = a;    printf("%d %d\n", n / g, n - g);    return 0;}
Craykonmain.cray · 14 行
수들 = 控制台.读数字();n = 수들[0];k = 수들[1];// 유클리드 호제법: b가 0이 될 때까지 (a, b)를 (b, a % b)로 바꾼다a = n;b = k;当 (b != 0){    r = a % b; // 나머지를 먼저 담아 둔다    a = b;    b = r;}g = a;控制台.输出(数学.商(n, g) + " " + (n - g));