{ }codelper
合計64ビット整数文字列の分割P0268

小計が書かれたレシートの検算

卸売市場のレシートには、買った品物の行の合間に小計の行と合計の行が書かれている。
ところが書かれた小計や合計に間違いがあるので、検算したい。
一行目にレシートの行数 n が与えられ、続く n 行にレシートが上から一行ずつ与えられる。
行は次の三つのどれかで、単語と数は空白一つで区切られている。
"ITEM p q" は一個 p ウォンの品物を q 個買った行で、この行の代金は p × q ウォンである。
"SUB s" は小計の行である。
すぐ前の SUB 行の次から(前に SUB 行がなければレシートの最初から)この行までに出た ITEM 行の代金をすべて足すと s ウォンだと書いたものである。
"TOTAL s" は合計の行である。
レシートの最初からこの行までに出た ITEM 行の代金をすべて足すと s ウォンだと書いたものである。
TOTAL 行は小計を数える区切りを切らない。
SUB 行と TOTAL 行に書かれた数は代金ではないので、どの合計にも足さない。
レシートの最後の行はいつも TOTAL 行である。
SUB 行と TOTAL 行ごとに、上から一行ずつ出力せよ。
書かれた値が実際の合計と同じなら "OK" を、違えば "FIX" と実際の合計を空白一つで区切って出力する。
実際の合計はカンマなしで数字だけを書く。

入力
1 ≤ n ≤ 10,000、1 ≤ p ≤ 10,000,000、1 ≤ q ≤ 1,000、0 ≤ s ≤ 10^15(すべて整数、先頭に 0 を付けない)
出力
SUB・TOTAL 行ごとに一行:OK または FIX と実際の合計
例 1 · stdin
6
ITEM 1200 3
ITEM 500 2
SUB 4600
ITEM 9900 1
SUB 9000
TOTAL 14500
stdout
OK
FIX 9900
OK
例 2 · stdin
4
SUB 0
ITEM 10000000 1000
SUB 10000000000
TOTAL 1410065408
stdout
OK
OK
FIX 10000000000
main.py
入力(stdin)

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

$ 準備完了

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

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

合計を二つ持って ITEM 行ごとに両方に足し、SUB 行で小計だけ 0 に戻せばよいです。
一つ目の落とし穴は大きさです。
一行の代金は 10^10 まで、合計は 10^14 までなので、C・C++ は long long、Java・C# は long が必要です。
合計だけ long にして int p, q を掛けると、掛けた瞬間に int であふれます(例 2 の 1410065408 は 10^10 があふれた値です)。
掛ける前に片方を long に変えます。
Python の int はあふれず、JS・TS・Craykon の数は 2^53(約 9×10^15)まで正確なので 10^14 は大丈夫です。
二つ目の落とし穴は、行を分けたかけらが文字列だということです。
Python int(...)、JS・TS Number(...)、Java Long.parseLong、C# long.Parse、Craykon .数に() で変えなければなりません。
JS・TS では "5" === 5 が false なので、変えずに比べるとすべて FIX になり、Craykon は文字列どうしを掛けようとすると止まります。
単語の比べ方も言語ごとに違います。
Java は kind.equals("ITEM")(== は同じオブジェクトかを見ます)、C は strcmp(kind, "ITEM") == 0 で、ほかは ==(JS・TS は ===)で比べられます。
C は scanf("%7s", kind) のように単語と数を一度に読めます。
三つ目に、書かれた小計を合計に足したり、TOTAL 行で小計を 0 に戻したりすると、後ろの検算がすべて狂います。

Pythonmain.py · 14 行
n = int(input())sub = 0    # 지난 SUB 뒤로 산 물건값의 합total = 0  # 처음부터 산 물건값의 합for _ in range(n):    kind, *nums = input().split()    if kind == "ITEM":        amount = int(nums[0]) * int(nums[1])        sub += amount        total += amount    else:        actual = sub if kind == "SUB" else total        print("OK" if int(nums[0]) == actual else f"FIX {actual}")        if kind == "SUB":            sub = 0  # 소계는 구역마다 다시 센다. TOTAL은 구역을 끊지 않는다
JavaScriptmain.js · 18 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);// 합계는 10^14까지: 2^53(약 9×10^15) 안이라 Number로 정확하다let sub = 0, total = 0;const out = [];for (let i = 1; i <= n; i++) {  const [kind, a, b] = lines[i].trim().split(/\s+/);  if (kind === "ITEM") {    const amount = Number(a) * Number(b);    sub += amount;    total += amount;  } else {    const actual = kind === "SUB" ? sub : total;    out.push(Number(a) === actual ? "OK" : `FIX ${actual}`); // 문자열 a와 수를 ===로 견주면 늘 false    if (kind === "SUB") sub = 0;  }}console.log(out.join("\n"));
TypeScriptmain.ts · 19 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);// 합계는 10^14까지: 2^53(약 9×10^15) 안이라 number로 정확하다let sub = 0, total = 0;const out: string[] = [];for (let i = 1; i <= n; i++) {  const [kind, a, b] = lines[i].trim().split(/\s+/);  if (kind === "ITEM") {    const amount: number = Number(a) * Number(b);    sub += amount;    total += amount;  } else {    const actual: number = kind === "SUB" ? sub : total;    out.push(Number(a) === actual ? "OK" : `FIX ${actual}`);    if (kind === "SUB") sub = 0;  }}console.log(out.join("\n"));
JavaMain.java · 29 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(in.readLine().trim());        long sub = 0, total = 0; // 합계는 10^14까지: int로는 넘친다        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            StringTokenizer st = new StringTokenizer(in.readLine());            String kind = st.nextToken();            if (kind.equals("ITEM")) { // 문자열은 ==가 아니라 equals로 견준다                // 값 × 개수도 10^10까지: 곱하기 전에 long으로                long amount = Long.parseLong(st.nextToken()) * Long.parseLong(st.nextToken());                sub += amount;                total += amount;            } else {                long actual = kind.equals("SUB") ? sub : total;                long claim = Long.parseLong(st.nextToken());                out.append(claim == actual ? "OK" : "FIX " + actual).append('\n');                if (kind.equals("SUB")) sub = 0;            }        }        System.out.print(out);    }}
C#Main.cs · 30 行
using System;using System.Text; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine());        long sub = 0, total = 0; // 합계는 10^14까지: int로는 넘친다        var out_ = new StringBuilder();        for (int i = 0; i < n; i++)        {            string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            if (p[0] == "ITEM")            {                // 값 × 개수도 10^10까지: 곱하기 전에 long으로                long amount = long.Parse(p[1]) * long.Parse(p[2]);                sub += amount;                total += amount;            }            else            {                long actual = p[0] == "SUB" ? sub : total;                out_.Append(long.Parse(p[1]) == actual ? "OK" : "FIX " + actual).Append('\n');                if (p[0] == "SUB") sub = 0;            }        }        Console.Write(out_);    }}
C++main.cpp · 26 行
#include <iostream>#include <string> int main() {    std::ios::sync_with_stdio(false);    int n;    std::cin >> n;    long long sub = 0, total = 0; // 합계는 10^14까지: int로는 넘친다    for (int i = 0; i < n; i++) {        std::string kind;        std::cin >> kind;        if (kind == "ITEM") {            long long p, q; // 값 × 개수도 10^10까지            std::cin >> p >> q;            sub += p * q;            total += p * q;        } else {            long long claim;            std::cin >> claim;            long long actual = kind == "SUB" ? sub : total;            if (claim == actual) std::cout << "OK\n";            else std::cout << "FIX " << actual << '\n';            if (kind == "SUB") sub = 0;        }    }}
Cmain.c · 27 行
#include <stdio.h>#include <string.h> int main(void) {    int n;    scanf("%d", &n);    long long sub = 0, total = 0; /* 합계는 10^14까지: int로는 넘친다 */    for (int i = 0; i < n; i++) {        char kind[8];        scanf("%7s", kind);        if (strcmp(kind, "ITEM") == 0) { /* 문자열은 ==가 아니라 strcmp로 견준다 */            long long p, q; /* 값 × 개수도 10^10까지 */            scanf("%lld %lld", &p, &q);            sub += p * q;            total += p * q;        } else {            long long claim;            scanf("%lld", &claim);            int is_sub = strcmp(kind, "SUB") == 0;            long long actual = is_sub ? sub : total;            if (claim == actual) puts("OK");            else printf("FIX %lld\n", actual);            if (is_sub) sub = 0;        }    }    return 0;}
Craykonmain.cray · 34 行
n = コンソール.行読み().数に();sub = 0; // 지난 SUB 뒤로 산 물건값의 합total = 0; // 처음부터 산 물건값의 합(10^14까지: 2^53 안이라 정확하다)繰り返す (i = 0; i < n; i++){    p = コンソール.行読み().分ける();    もし (p[0] == "ITEM")    {        // 나눈 조각은 문자열이다: 수로 바꾼 다음 곱한다        amount = p[1].数に() * p[2].数に();        sub += amount;        total += amount;    }    でなければ    {        actual = total;        もし (p[0] == "SUB")        {            actual = sub;        }        もし (p[1].数に() == actual)        {            コンソール.書く("OK");        }        でなければ        {            コンソール.書く("FIX " + actual);        }        もし (p[0] == "SUB")        {            sub = 0;        }    }}