{ }codelper
キューシミュレーション64ビット整数P0309

ひびが入ったら冷まして並び直す窯の列

陶芸工房に窯がk個あり、1番からk番まで番号が付いている。
n個の器が工房に来る。
i番目の器はaᵢ秒に来て、窯の前の列の最後尾に並ぶ。
窯は一度に器を1個だけ焼き、i番目の器を1回焼くのにbᵢ秒かかる。
i番目の器は、最初のcᵢ回の焼きでは毎回ひびが入る。
ひびが入った器は、その焼きが終わった時刻からr秒冷ましたあと、列の最後尾に並び直す。
その次の焼き、つまりcᵢ + 1回目の焼きではひびが入らず、その焼きが終わった時刻が器の完成時刻である。
最初はどの窯も空いている。
同じ時刻Tには、次の順に物事が起こる。
① T秒に焼きが終わる窯がすべて空く。
② T秒に冷まし終わる器が列に並ぶ。
そのような器が複数あれば、番号の小さい窯から出たものが先に並ぶ(冷ます時間はどれも同じなので、同じ時刻に焼きが終わった器どうしである)。
③ T秒に来る新しい器が入力の順に列に並ぶ。
④ 空いた窯と並んでいる器の両方がある間、空いた窯のうち番号が最も小さい窯が列の先頭の器を焼き始める。
rが0なら、ひびが入った器は焼きが終わったその時刻の②で列に並ぶ。
器ごとの完成時刻と、窯ごとの焼き始めた回数を出力せよ。

入力
1行目に器の数n(1 ≤ n ≤ 10,000)、窯の数k(1 ≤ k ≤ 8)、冷ます時間r(0 ≤ r ≤ 1,000,000,000)が空白1つで区切られて与えられる。
続くn行のi行目に整数aᵢ(0 ≤ aᵢ ≤ 1,000,000,000)、bᵢ(1 ≤ bᵢ ≤ 10,000)、cᵢ(0 ≤ cᵢ ≤ 10)が空白1つで区切られて与えられる。
aᵢは減らない順に与えられる(a₁ ≤ a₂ ≤ … ≤ aₙ)。
出力
最初のn行のi行目に、i番目の器の完成時刻を出力する。
最後の行に、1番の窯からk番の窯まで、焼き始めた回数を空白1つで区切って出力する。
数はカンマなしで出力する。
例 1 · stdin
4 2 3
0 2 1
0 6 0
1 3 0
5 1 0
stdout
7
6
5
7
3 2
main.py
入力(stdin)

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

$ 準備完了

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

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

キュー2つで解くシミュレーションです。
窯の前の列がキューなのはすぐ分かりますが、冷ましている器もキューになる点が要です。
冷ます時間rはどの器も同じなので、先に冷まし始めた器が先に冷め終わります。
だからソートもヒープも要らず、ひびが入った順にキューへ入れて先頭だけを見れば済みます。
時刻は1秒ずつ数えず、「次に何かが起こる時刻」へ飛びます。
次の器が来る時刻、冷ましキューの先頭が冷め終わる時刻、k個の窯が終わる時刻のうち最も早いものです。
例では0秒に器1・2が窯1・2に入ります。
2秒に器1にひびが入って5秒まで冷まし、並んでいた器3が窯1に入って5秒に終わります。
5秒には窯1だけが空き(窯2は6秒まで焼いています)、冷まし終わった器1が先に、いま来た器4がその後に並びます。
だから器1が窯1に入って7秒に完成し、器4は6秒に空いた窯2に入って7秒に完成します。
罠1:同じ時刻に冷まし終わった器と新しい器が重なったら、冷ました器が先です。
順を逆にすると、例では器4が5秒に窯1を取って6秒に終わり、器1は6秒にやっと入って8秒に終わります。
窯ごとの回数も4 1に変わります。
罠2:ひびが入った器をすぐ列の最後尾に並べてはいけません。
並べるのはr秒後で、その間に来た器が前に並びます。
罠3:同じ時刻では、窯を空けてから列を詰めます。
先に詰めると、いま終わった窯が次の出来事まで空いたままになります。
罠4:空いた窯が複数あれば番号の小さい窯から使います。
完成時刻が同じでも窯ごとの回数が変わります。
罠5:時刻は32ビット整数を超えます。
1,000,000,000秒に来た器が1,000,000,000秒ずつ10回冷ますと110億を超えます。
CとC++はlong long、JavaとC#はlongが必要です。
Pythonの整数と、JavaScript・TypeScript・Craykonの数(2^53まで正確)はそのままで大丈夫です。
罠6:器は合計で110,000回まで列に入ります。
Craykonのリストは10,000個までなので、追加するだけのリストではキューを作れません。
同時に並ぶ器はn個を超えないので、n個分のリストを輪のように使い、位置をnで割った余りで巻き戻します。
巻き戻さないとリストの外を指して止まります。
キューの書き方は言語ごとに違います。
Pythonはcollections.dequeのappendとpopleftを使います。
リストのpop(0)は後ろの要素をすべて詰めるので遅いです。
JavaはArrayDeque、C#はQueue<int>、C++はstd::queueを使います。
JavaScriptとTypeScriptには専用のキューがないので、配列と先頭の番号で作りました。
shift()は配列が長いと遅くなることがあります。
Cは配列・先頭の番号・個数で輪のキューを自分で作ります。
Craykonも新リスト(n, 0)で作ったリストを同じように使います。

Pythonmain.py · 58 行
import sysfrom collections import deque data = sys.stdin.buffer.read().split()n, k, rest = int(data[0]), int(data[1]), int(data[2])arrive = [int(data[3 + 3 * i]) for i in range(n)]bake = [int(data[4 + 3 * i]) for i in range(n)]left = [int(data[5 + 3 * i]) for i in range(n)]   # 앞으로 금이 갈 횟수 end = [-1] * k        # 가마가 굽기를 끝내는 시각(-1이면 비어 있다)cur = [0] * k         # 가마가 굽고 있는 도자기count = [0] * k       # 가마마다 구운 횟수ready = [0] * n       # 식힘이 끝나 다시 줄에 서는 시각finish = [0] * ncooling = deque()     # 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다line = deque()        # 가마 앞의 줄nxt = 0               # 아직 오지 않은 첫 도자기done = 0while done < n:    # 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것    t = -1    if nxt < n:        t = arrive[nxt]    if cooling and (t < 0 or ready[cooling[0]] < t):        t = ready[cooling[0]]    for j in range(k):        if end[j] >= 0 and (t < 0 or end[j] < t):            t = end[j]    # ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)    for j in range(k):        if end[j] == t:            i = cur[j]            end[j] = -1            if left[i] > 0:                left[i] -= 1                ready[i] = t + rest                cooling.append(i)            else:                finish[i] = t                done += 1    # ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다    while cooling and ready[cooling[0]] == t:        line.append(cooling.popleft())    while nxt < n and arrive[nxt] == t:        line.append(nxt)        nxt += 1    # ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다    for j in range(k):        if not line:            break        if end[j] < 0:            i = line.popleft()            cur[j] = i            end[j] = t + bake[i]            count[j] += 1 print("\n".join(map(str, finish)))print(" ".join(map(str, count)))
JavaScriptmain.js · 73 行
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, k, rest] = data;const arrive = [];const bake = [];const left = []; // 앞으로 금이 갈 횟수for (let i = 0; i < n; i++) {  arrive.push(data[3 + 3 * i]);  bake.push(data[4 + 3 * i]);  left.push(data[5 + 3 * i]);} // 큐: 배열과 머리 번호. shift()는 뒤의 것을 모두 당기니 머리 번호만 옮긴다class Queue {  constructor() {    this.items = [];    this.head = 0;  }  get size() {    return this.items.length - this.head;  }  front() {    return this.items[this.head];  }  push(x) {    this.items.push(x);  }  pop() {    return this.items[this.head++];  }} const end = new Array(k).fill(-1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다)const cur = new Array(k).fill(0); // 가마가 굽고 있는 도자기const count = new Array(k).fill(0); // 가마마다 구운 횟수const ready = new Array(n).fill(0); // 식힘이 끝나 다시 줄에 서는 시각const finish = new Array(n).fill(0);const cooling = new Queue(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다const line = new Queue(); // 가마 앞의 줄let next = 0; // 아직 오지 않은 첫 도자기let done = 0;while (done < n) {  // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것  let t = Infinity;  if (next < n) t = arrive[next];  if (cooling.size > 0) t = Math.min(t, ready[cooling.front()]);  for (let j = 0; j < k; j++) if (end[j] >= 0) t = Math.min(t, end[j]);  // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)  for (let j = 0; j < k; j++) {    if (end[j] !== t) continue;    const i = cur[j];    end[j] = -1;    if (left[i] > 0) {      left[i]--;      ready[i] = t + rest;      cooling.push(i);    } else {      finish[i] = t;      done++;    }  }  // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다  while (cooling.size > 0 && ready[cooling.front()] === t) line.push(cooling.pop());  while (next < n && arrive[next] === t) line.push(next++);  // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다  for (let j = 0; j < k && line.size > 0; j++) {    if (end[j] >= 0) continue;    const i = line.pop();    cur[j] = i;    end[j] = t + bake[i];    count[j]++;  }}console.log(finish.join("\n") + "\n" + count.join(" "));
TypeScriptmain.ts · 73 行
import { readFileSync } from "node:fs"; const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, k, rest] = data;const arrive: number[] = [];const bake: number[] = [];const left: number[] = []; // 앞으로 금이 갈 횟수for (let i = 0; i < n; i++) {  arrive.push(data[3 + 3 * i]);  bake.push(data[4 + 3 * i]);  left.push(data[5 + 3 * i]);} // 큐: 배열과 머리 번호. shift()는 뒤의 것을 모두 당기니 머리 번호만 옮긴다class Queue<T> {  private readonly items: T[] = [];  private head = 0;  get size(): number {    return this.items.length - this.head;  }  front(): T {    return this.items[this.head];  }  push(x: T): void {    this.items.push(x);  }  pop(): T {    return this.items[this.head++];  }} const end: number[] = new Array<number>(k).fill(-1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다)const cur: number[] = new Array<number>(k).fill(0); // 가마가 굽고 있는 도자기const count: number[] = new Array<number>(k).fill(0); // 가마마다 구운 횟수const ready: number[] = new Array<number>(n).fill(0); // 식힘이 끝나 다시 줄에 서는 시각const finish: number[] = new Array<number>(n).fill(0);const cooling = new Queue<number>(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다const line = new Queue<number>(); // 가마 앞의 줄let next = 0; // 아직 오지 않은 첫 도자기let done = 0;while (done < n) {  // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것  let t = Infinity;  if (next < n) t = arrive[next];  if (cooling.size > 0) t = Math.min(t, ready[cooling.front()]);  for (let j = 0; j < k; j++) if (end[j] >= 0) t = Math.min(t, end[j]);  // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)  for (let j = 0; j < k; j++) {    if (end[j] !== t) continue;    const i: number = cur[j];    end[j] = -1;    if (left[i] > 0) {      left[i]--;      ready[i] = t + rest;      cooling.push(i);    } else {      finish[i] = t;      done++;    }  }  // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다  while (cooling.size > 0 && ready[cooling.front()] === t) line.push(cooling.pop());  while (next < n && arrive[next] === t) line.push(next++);  // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다  for (let j = 0; j < k && line.size > 0; j++) {    if (end[j] >= 0) continue;    const i: number = line.pop();    cur[j] = i;    end[j] = t + bake[i];    count[j]++;  }}console.log(finish.join("\n") + "\n" + count.join(" "));
JavaMain.java · 65 行
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));        st.nextToken(); int n = (int) st.nval;        st.nextToken(); int k = (int) st.nval;        st.nextToken(); long rest = (long) st.nval;        long[] arrive = new long[n];        int[] bake = new int[n], left = new int[n]; // left: 앞으로 금이 갈 횟수        for (int i = 0; i < n; i++) {            st.nextToken(); arrive[i] = (long) st.nval;            st.nextToken(); bake[i] = (int) st.nval;            st.nextToken(); left[i] = (int) st.nval;        }         long[] end = new long[k];      // 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다        Arrays.fill(end, -1);        int[] cur = new int[k];        // 가마가 굽고 있는 도자기        int[] count = new int[k];      // 가마마다 구운 횟수        long[] ready = new long[n];    // 식힘이 끝나 다시 줄에 서는 시각        long[] finish = new long[n];        ArrayDeque<Integer> cooling = new ArrayDeque<>(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다        ArrayDeque<Integer> line = new ArrayDeque<>();    // 가마 앞의 줄        int next = 0, done = 0;        while (done < n) {            // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것            long t = Long.MAX_VALUE;            if (next < n) t = arrive[next];            if (!cooling.isEmpty()) t = Math.min(t, ready[cooling.peekFirst()]);            for (int j = 0; j < k; j++) if (end[j] >= 0) t = Math.min(t, end[j]);            // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)            for (int j = 0; j < k; j++) {                if (end[j] != t) continue;                int i = cur[j];                end[j] = -1;                if (left[i] > 0) {                    left[i]--;                    ready[i] = t + rest;                    cooling.addLast(i);                } else {                    finish[i] = t;                    done++;                }            }            // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다            while (!cooling.isEmpty() && ready[cooling.peekFirst()] == t) line.addLast(cooling.pollFirst());            while (next < n && arrive[next] == t) line.addLast(next++);            // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다            for (int j = 0; j < k && !line.isEmpty(); j++) {                if (end[j] >= 0) continue;                int i = line.pollFirst();                cur[j] = i;                end[j] = t + bake[i];                count[j]++;            }        }         StringBuilder sb = new StringBuilder();        for (long x : finish) sb.append(x).append('\n');        for (int j = 0; j < k; j++) sb.append(j > 0 ? " " : "").append(count[j]);        System.out.println(sb);    }}
C#Main.cs · 75 行
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        var data = Console.In.ReadToEnd().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(data[0]), k = int.Parse(data[1]);        long rest = long.Parse(data[2]);        var arrive = new long[n];        var bake = new int[n];        var left = new int[n]; // 앞으로 금이 갈 횟수        for (int i = 0; i < n; i++)        {            arrive[i] = long.Parse(data[3 + 3 * i]);            bake[i] = int.Parse(data[4 + 3 * i]);            left[i] = int.Parse(data[5 + 3 * i]);        }         var end = new long[k];       // 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다        Array.Fill(end, -1L);        var cur = new int[k];        // 가마가 굽고 있는 도자기        var count = new int[k];      // 가마마다 구운 횟수        var ready = new long[n];     // 식힘이 끝나 다시 줄에 서는 시각        var finish = new long[n];        var cooling = new Queue<int>(); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다        var line = new Queue<int>();    // 가마 앞의 줄        int next = 0, done = 0;        while (done < n)        {            // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것            long t = long.MaxValue;            if (next < n) t = arrive[next];            if (cooling.Count > 0) t = Math.Min(t, ready[cooling.Peek()]);            for (int j = 0; j < k; j++) if (end[j] >= 0) t = Math.Min(t, end[j]);            // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)            for (int j = 0; j < k; j++)            {                if (end[j] != t) continue;                int i = cur[j];                end[j] = -1;                if (left[i] > 0)                {                    left[i]--;                    ready[i] = t + rest;                    cooling.Enqueue(i);                }                else                {                    finish[i] = t;                    done++;                }            }            // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다            while (cooling.Count > 0 && ready[cooling.Peek()] == t) line.Enqueue(cooling.Dequeue());            while (next < n && arrive[next] == t) line.Enqueue(next++);            // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다            for (int j = 0; j < k && line.Count > 0; j++)            {                if (end[j] >= 0) continue;                int i = line.Dequeue();                cur[j] = i;                end[j] = t + bake[i];                count[j]++;            }        }         var sb = new StringBuilder();        foreach (long x in finish) sb.Append(x).Append('\n');        sb.Append(string.Join(" ", count)).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 67 行
#include <algorithm>#include <iostream>#include <queue>#include <vector>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int n, k;    long long rest;    cin >> n >> k >> rest;    vector<long long> arrive(n);    vector<int> bake(n), left(n); // left: 앞으로 금이 갈 횟수    for (int i = 0; i < n; i++) cin >> arrive[i] >> bake[i] >> left[i];     vector<long long> end(k, -1);   // 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다    vector<int> cur(k, 0);          // 가마가 굽고 있는 도자기    vector<int> count(k, 0);        // 가마마다 구운 횟수    vector<long long> ready(n, 0);  // 식힘이 끝나 다시 줄에 서는 시각    vector<long long> finish(n, 0);    queue<int> cooling; // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다    queue<int> line;    // 가마 앞의 줄    int next = 0, done = 0;    while (done < n) {        // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것        long long t = -1;        if (next < n) t = arrive[next];        if (!cooling.empty() && (t < 0 || ready[cooling.front()] < t)) t = ready[cooling.front()];        for (int j = 0; j < k; j++)            if (end[j] >= 0 && (t < 0 || end[j] < t)) t = end[j];        // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)        for (int j = 0; j < k; j++) {            if (end[j] != t) continue;            int i = cur[j];            end[j] = -1;            if (left[i] > 0) {                left[i]--;                ready[i] = t + rest;                cooling.push(i);            } else {                finish[i] = t;                done++;            }        }        // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다        while (!cooling.empty() && ready[cooling.front()] == t) {            line.push(cooling.front());            cooling.pop();        }        while (next < n && arrive[next] == t) line.push(next++);        // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다        for (int j = 0; j < k && !line.empty(); j++) {            if (end[j] >= 0) continue;            int i = line.front();            line.pop();            cur[j] = i;            end[j] = t + bake[i];            count[j]++;        }    }     for (long long x : finish) cout << x << '\n';    for (int j = 0; j < k; j++) cout << (j ? " " : "") << count[j];    cout << '\n';    return 0;}
Cmain.c · 82 行
#include <stdio.h> #define MAXN 10000#define MAXK 8 static long long arrive[MAXN], ready[MAXN], finish[MAXN];static int bake[MAXN], left[MAXN]; /* left: 앞으로 금이 갈 횟수 */ /* 큐: 둥근 배열. 한 번에 n개보다 많이 들어 있지 않으니 MAXN칸을 돌려 쓴다 */typedef struct {    int items[MAXN];    int head, size;} Queue; static void push(Queue *q, int x) {    q->items[(q->head + q->size) % MAXN] = x;    q->size++;} static int pop(Queue *q) {    int x = q->items[q->head];    q->head = (q->head + 1) % MAXN;    q->size--;    return x;} static Queue cooling; /* 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다 */static Queue line;    /* 가마 앞의 줄 */ int main(void) {    int n, k;    long long rest;    if (scanf("%d %d %lld", &n, &k, &rest) != 3) return 1;    for (int i = 0; i < n; i++) scanf("%lld %d %d", &arrive[i], &bake[i], &left[i]);     long long end[MAXK]; /* 가마가 굽기를 끝내는 시각(-1이면 비어 있다). 시각은 int를 넘는다 */    int cur[MAXK], count[MAXK];    for (int j = 0; j < k; j++) {        end[j] = -1;        cur[j] = 0;        count[j] = 0;    }    int next = 0, done = 0;    while (done < n) {        /* 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것 */        long long t = -1;        if (next < n) t = arrive[next];        if (cooling.size > 0 && (t < 0 || ready[cooling.items[cooling.head]] < t)) t = ready[cooling.items[cooling.head]];        for (int j = 0; j < k; j++)            if (end[j] >= 0 && (t < 0 || end[j] < t)) t = end[j];        /* ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터) */        for (int j = 0; j < k; j++) {            if (end[j] != t) continue;            int i = cur[j];            end[j] = -1;            if (left[i] > 0) {                left[i]--;                ready[i] = t + rest;                push(&cooling, i);            } else {                finish[i] = t;                done++;            }        }        /* ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다 */        while (cooling.size > 0 && ready[cooling.items[cooling.head]] == t) push(&line, pop(&cooling));        while (next < n && arrive[next] == t) push(&line, next++);        /* ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다 */        for (int j = 0; j < k && line.size > 0; j++) {            if (end[j] >= 0) continue;            int i = pop(&line);            cur[j] = i;            end[j] = t + bake[i];            count[j]++;        }    }     for (int i = 0; i < n; i++) printf("%lld\n", finish[i]);    for (int j = 0; j < k; j++) printf(j ? " %d" : "%d", count[j]);    printf("\n");    return 0;}
Craykonmain.cray · 106 行
첫줄 = コンソール.数読み();n = 첫줄[0];k = 첫줄[1];rest = 첫줄[2];arrive = [];bake = [];left = []; // 앞으로 금이 갈 횟수繰り返す (i = 0; i < n; i++){    줄 = コンソール.数読み();    arrive.追加(줄[0]);    bake.追加(줄[1]);    left.追加(줄[2]);} end = 新リスト(k, -1); // 가마가 굽기를 끝내는 시각(-1이면 비어 있다)cur = 新リスト(k, 0); // 가마가 굽고 있는 도자기count = 新リスト(k, 0); // 가마마다 구운 횟수ready = 新リスト(n, 0); // 식힘이 끝나 다시 줄에 서는 시각finish = 新リスト(n, 0); // 큐 두 개를 둥근 목록으로 만든다. 목록은 10,000칸까지라 끝없이 넣기만 할 수 없고,// 한 번에 n개보다 많이 들어 있지 않으니 n칸을 돌려 쓴다cooling = 新リスト(n, 0); // 식히는 도자기: 넣은 차례가 곧 식힘이 끝나는 차례다coolHead = 0;coolSize = 0;line = 新リスト(n, 0); // 가마 앞의 줄lineHead = 0;lineSize = 0; next = 0; // 아직 오지 않은 첫 도자기done = 0;間 (done < n){    // 다음에 무엇인가 일어나는 시각: 새 도자기, 식힘 끝, 굽기 끝 가운데 가장 이른 것    t = -1;    もし (next < n)    {        t = arrive[next];    }    もし (coolSize > 0 && (t < 0 || ready[cooling[coolHead]] < t))    {        t = ready[cooling[coolHead]];    }    繰り返す (j = 0; j < k; j++)    {        もし (end[j] >= 0 && (t < 0 || end[j] < t))        {            t = end[j];        }    }    // ① 굽기가 끝난 가마가 빈다(번호가 작은 가마부터)    繰り返す (j = 0; j < k; j++)    {        もし (end[j] == t)        {            i = cur[j];            end[j] = -1;            もし (left[i] > 0)            {                left[i] -= 1;                ready[i] = t + rest;                cooling[(coolHead + coolSize) % n] = i;                coolSize += 1;            }            でなければ            {                finish[i] = t;                done += 1;            }        }    }    // ②③ 식힘을 마친 도자기가 먼저, 새로 온 도자기가 그다음에 줄을 선다    間 (coolSize > 0 && ready[cooling[coolHead]] == t)    {        line[(lineHead + lineSize) % n] = cooling[coolHead];        lineSize += 1;        coolHead = (coolHead + 1) % n;        coolSize -= 1;    }    間 (next < n && arrive[next] == t)    {        line[(lineHead + lineSize) % n] = next;        lineSize += 1;        next += 1;    }    // ④ 번호가 작은 빈 가마부터 줄 맨 앞 도자기를 굽는다    繰り返す (j = 0; j < k && lineSize > 0; j++)    {        もし (end[j] < 0)        {            i = line[lineHead];            lineHead = (lineHead + 1) % n;            lineSize -= 1;            cur[j] = i;            end[j] = t + bake[i];            count[j] += 1;        }    }} それぞれ (x の中 finish){    コンソール.書く(x);}コンソール.書く(count.つなぐ(" "));