{ }codelper
剰余負の剰余64ビット整数P0266

輪ごとに目の数が違うカウンター

古い機械式カウンターに、輪が n 個一列に付いている。
左から i 番目の輪には 0 から b_i - 1 までの目があり、今は d_i の目を指している。
カウンターを前に一目回すと、いちばん右の輪が 1 増える。
ある輪が b_i - 1 からさらに一目進むと 0 に戻り、その左の輪が 1 増える。
いちばん左の輪が b_1 - 1 から 0 に戻ったら、カウンター全体が前に一回り越えたことになる。
後ろに一目回すと逆に動く。
いちばん右の輪が 1 減り、0 からさらに一目進むと b_i - 1 になって、その左の輪が 1 減る。
いちばん左の輪が 0 から b_1 - 1 になったら、カウンター全体が後ろに一回り越えたことになる。
t が正なら前に t 目、負なら後ろに -t 目回し、0 なら回さない。
一行目に n、二行目に b_1 … b_n、三行目に d_1 … d_n、四行目に t が与えられる。
同じ行の数は空白一つで区切られている。
一行目には、回し終えたあと輪が指す目を左の輪から空白一つずつ区切って出力せよ。
二行目には、カウンター全体が一回り越えた回数を出力せよ。
前に越えたならその回数を、後ろに越えたならその回数に - を付けて書き、越えなかったなら 0 を書く。

入力
1 ≤ n ≤ 10、2 ≤ b_i ≤ 60、0 ≤ d_i < b_i、-10^12 ≤ t ≤ 10^12(すべて整数)
出力
一行目:n 個の輪の目、二行目:越えた回数(後ろなら負)
例 1 · stdin
3
24 60 60
23 59 50
15
stdout
0 0 5
1
例 2 · stdin
2
7 3
0 1
-5
stdout
5 2
-1
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

いちばん右の輪から、入ってきた値 s = d +(受け取った値)を、目 s mod b と左へ渡す商に分ければよいです。
後ろに回すと s が負になり、言語によって % と / が違います。
Python の %・// は切り捨て除算なので、目はいつも 0 ~ b-1、商も下に丸めた値(-4 // 3 == -2)で、そのまま正しいです。
C・C++・Java・C#・JS・TS・Craykon の % は割られる数の符号に従い(-4 % 3 == -1)、C・C++・Java・C# の整数の /、JS・TS の Math.trunc(s / b)、Craykon の 数学.商 は 0 の方へ切り捨てる(-4 / 3 == -1)ので、目が負になり、借りが一つ足りなくなります。
JS・TS・Craykon の / は小数まで割る(-4 / 3 == -1.333…)ので、そのまま商には使えません。
(s % b + b) % b で目を直してから、商は (s - 目) / b で求めるか(ちょうど割り切れます)、Java なら Math.floorMod・Math.floorDiv を使います。
目だけ直して商を 0 の方へ切り捨てる割り算のままにするのもよくある間違いです。
二つ目の落とし穴は大きさです。
t は 10^12 までなので C・C++ は long long、Java・C# は long が必要で、int に入れるとあふれます。
JS・TS・Craykon の数は 2^53(約 9×10^15)まで正確なので t は大丈夫です。
しかしカウンター全体を一つの数に直して計算すると、輪の目の数の積が 60^10(約 6×10^17)まで大きくなって 2^53 を超え、下の桁が狂います。
輪ごとに余りと商で渡せば、どの言語でも値は t の近くにとどまります。
Python の int(s / b) も実数の割り算のあと 0 の方へ切り捨てるので同じ落とし穴です。

Pythonmain.py · 14 行
n = int(input())b = list(map(int, input().split()))d = list(map(int, input().split()))t = int(input()) # 오른쪽 바퀴부터: 이 바퀴에 들어온 값 s를 칸(나머지)과 왼쪽으로 넘길 값(몫)으로 나눈다# Python의 %와 //는 내림 나눗셈이라 s가 음수여도 칸은 0 ~ b-1, 몫은 아래로 내린 값이다carry = tfor i in range(n - 1, -1, -1):    s = d[i] + carry    d[i] = s % b[i]    carry = s // b[i]print(*d)print(carry)  # 맨 왼쪽 바퀴에서도 넘친 값 = 계수기 전체가 넘어간 횟수
JavaScriptmain.js · 13 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const b = lines[1].trim().split(/\s+/).map(Number);const d = lines[2].trim().split(/\s+/).map(Number);let carry = Number(lines[3]); // 10^12는 2^53 안이라 Number로 정확하다 // JS의 %는 나누어지는 수의 부호를 따른다(-7 % 3 === -1) → b를 더해 다시 나눈다for (let i = n - 1; i >= 0; i--) {  const s = d[i] + carry;  d[i] = ((s % b[i]) + b[i]) % b[i];  carry = (s - d[i]) / b[i]; // s - 칸은 b로 딱 나누어떨어진다: 내림 몫}console.log(d.join(" ") + "\n" + carry);
TypeScriptmain.ts · 14 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);const b: number[] = lines[1].trim().split(/\s+/).map(Number);const d: number[] = lines[2].trim().split(/\s+/).map(Number);let carry: number = Number(lines[3]); // 10^12는 2^53 안이라 number로 정확하다 // TS(JS)의 %는 나누어지는 수의 부호를 따른다(-7 % 3 === -1) → b를 더해 다시 나눈다for (let i = n - 1; i >= 0; i--) {  const s: number = d[i] + carry;  d[i] = ((s % b[i]) + b[i]) % b[i];  carry = (s - d[i]) / b[i]; // s - 칸은 b로 딱 나누어떨어진다: 내림 몫}console.log(d.join(" ") + "\n" + carry);
JavaMain.java · 24 行
import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int n = sc.nextInt();        int[] b = new int[n];        long[] d = new long[n];        for (int i = 0; i < n; i++) b[i] = sc.nextInt();        for (int i = 0; i < n; i++) d[i] = sc.nextLong();        long carry = sc.nextLong(); // 10^12: int로는 넘친다         // Java의 %와 /는 0 쪽으로 버린다(-7 % 3 == -1) → Math.floorMod·floorDiv는 내림으로 나눈다        for (int i = n - 1; i >= 0; i--) {            long s = d[i] + carry;            d[i] = Math.floorMod(s, (long) b[i]);            carry = Math.floorDiv(s, (long) b[i]);        }        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) out.append(i > 0 ? " " : "").append(d[i]);        out.append('\n').append(carry).append('\n');        System.out.print(out);    }}
C#Main.cs · 23 行
using System;using System.Linq; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine());        int[] b = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();        long[] d = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(long.Parse).ToArray();        long carry = long.Parse(Console.ReadLine().Trim()); // 10^12: int로는 넘친다         // C#의 %는 나누어지는 수의 부호를 따른다(-7 % 3 == -1) → b를 더해 다시 나눈다        for (int i = n - 1; i >= 0; i--)        {            long s = d[i] + carry;            d[i] = (s % b[i] + b[i]) % b[i];            carry = (s - d[i]) / b[i]; // s - 칸은 b로 딱 나누어떨어진다: 내림 몫        }        Console.WriteLine(string.Join(" ", d));        Console.WriteLine(carry);    }}
C++main.cpp · 21 行
#include <iostream>#include <vector> int main() {    int n;    std::cin >> n;    std::vector<long long> b(n), d(n);    for (auto& x : b) std::cin >> x;    for (auto& x : d) std::cin >> x;    long long carry; // t는 10^12까지: int로는 넘친다    std::cin >> carry;     // C++의 %는 0 쪽으로 버린 나눗셈의 나머지라 음수가 나올 수 있다 → b를 더해 다시 나눈다    for (int i = n - 1; i >= 0; i--) {        long long s = d[i] + carry;        d[i] = (s % b[i] + b[i]) % b[i];        carry = (s - d[i]) / b[i]; // s - 칸은 b로 딱 나누어떨어진다: 내림 몫    }    for (int i = 0; i < n; i++) std::cout << (i ? " " : "") << d[i];    std::cout << '\n' << carry << '\n';}
Cmain.c · 20 行
#include <stdio.h> int main(void) {    int n, b[10];    long long d[10], carry; /* t는 10^12까지: int로는 넘친다 */    scanf("%d", &n);    for (int i = 0; i < n; i++) scanf("%d", &b[i]);    for (int i = 0; i < n; i++) scanf("%lld", &d[i]);    scanf("%lld", &carry);     /* C의 %는 0 쪽으로 버린 나눗셈의 나머지라 음수가 나올 수 있다 → b를 더해 다시 나눈다 */    for (int i = n - 1; i >= 0; i--) {        long long s = d[i] + carry;        d[i] = (s % b[i] + b[i]) % b[i];        carry = (s - d[i]) / b[i]; /* s - 칸은 b로 딱 나누어떨어진다: 내림 몫 */    }    for (int i = 0; i < n; i++) printf(i ? " %lld" : "%lld", d[i]);    printf("\n%lld\n", carry);    return 0;}
Craykonmain.cray · 15 行
n = コンソール.行読み().数に();b = コンソール.数読み();d = コンソール.数読み();carry = コンソール.行読み().数に(); // 10^12는 2^53 안이라 정확하다 // 크레이콘의 %도 나누어지는 수의 부호를 따른다(-7 % 3 == -1) → b를 더해 다시 나눈다// 수학.몫은 0 쪽으로 버리니, 내림 몫은 (s - 칸) / b로 구한다(딱 나누어떨어진다)繰り返す (i = n - 1; i >= 0; i--){    s = d[i] + carry;    d[i] = (s % b[i] + b[i]) % b[i];    carry = (s - d[i]) / b[i];}コンソール.書く(d.つなぐ(" "));コンソール.書く(carry);