{ }codelper
우선순위 큐(힙)그대로 따라 하기큰 정수(64비트)P0277

기다릴수록 급해지는 진료 차례

응급실에 진료실이 k개 있고, 1번부터 k번까지 번호가 붙어 있다. 환자 n명이 1번부터 n번까지 온 차례대로 주어진다. i번 환자는 aᵢ분에 와서 줄에 서고, 처음 급한 정도는 sᵢ이며, 진료에 dᵢ분이 걸린다. 줄에서 기다리는 환자의 급한 정도는 1분마다 1씩 오른다.
곧 T분에 i번 환자의 급한 정도는 sᵢ + (T − aᵢ)이다. 같은 시각 T에는 다음 차례로 일이 일어난다. ① T분에 진료가 끝나는 진료실이 모두 빈다. ② T분에 오는 환자가 모두 줄에 선다. ③ 빈 진료실과 기다리는 환자가 모두 있는 동안, 빈 진료실 가운데 번호가 가장 작은 곳이 기다리는 환자 가운데 T분에 가장 급한 환자를 부른다.
급한 정도가 같으면 환자 번호가 작은 사람을 부른다. 불린 환자는 T분에 진료를 시작해 T + dᵢ분에 끝내고, 그 진료실은 그동안 다른 환자를 부르지 않는다. 처음(0분 전)에는 모든 진료실이 비어 있다. 첫 줄에 환자 번호를 불린 차례대로 출력하라. 둘째 줄에 모든 환자가 기다린 시간(진료를 시작한 시각 − 온 시각)의 합을 출력하라. 셋째 줄에 1번부터 k번 진료실까지 진료한 환자 수를 차례대로 출력하라.

입력
첫 줄에 n과 k, 다음 n줄에 i번 환자의 "aᵢ sᵢ dᵢ"가 빈칸 하나로 나뉘어 주어진다.
1 ≤ n ≤ 10,000, 1 ≤ k ≤ 100, 0 ≤ a₁ ≤ a₂ ≤ … ≤ aₙ ≤ 1,000,000, 1 ≤ sᵢ ≤ 1,000,000, 1 ≤ dᵢ ≤ 10,000.
모두 정수다
출력
세 줄: 불린 차례의 환자 번호 n개, 기다린 시간의 합(쉼표 없이 숫자만), 진료실마다 진료한 환자 수 k개.
같은 줄의 수는 빈칸 하나로 나눈다
예시 1 · stdin
5 2
0 3 4
0 5 2
1 4 3
2 6 5
2 5 1
stdout
2 1 4 3 5
8
3 2
main.py
입력(stdin)

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

$ 준비됐어요

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

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

급한 정도가 시간마다 바뀌니 힙에 넣을 수 없을 것 같지만, 기다리는 사람은 모두 같은 빠르기(1분에 1)로 오르니 두 사람의 차례는 T와 상관없이 sᵢ − aᵢ로 정해져요: sᵢ + (T − aᵢ) > sⱼ + (T − aⱼ) ⇔ sᵢ − aᵢ > sⱼ − aⱼ.
그래서 힙 세 개로 풀어요: 기다리는 환자(s − a가 큰 것, 같으면 번호가 작은 것), 빈 진료실(번호가 작은 것), 바쁜 진료실(끝나는 시각이 이른 것).
다음에 무엇인가 바뀌는 시각은 '다음 환자가 오는 시각'과 '가장 먼저 끝나는 진료' 가운데 이른 쪽이니, 그 시각으로 건너뛰며 ① ② ③을 해요.
부를 때마다 줄을 다 훑으면 n²이라 Python·크레이콘에 느려요.
예시: 4분에 3번(4 + 3 = 7)과 5번(5 + 2 = 7)이 같아서 번호가 작은 3번이 불려요.
7분에 두 방이 함께 비면 1번 방이 5번을 불러요.
함정 하나: 기다린 시간을 잊고 sᵢ만 보면 5번이 3번보다 먼저 불려 틀려요.
함정 둘: 같은 급한 정도에서 나중 번호를 부르면 틀려요.
함정 셋: 빈 방을 '먼저 빈 방부터'(큐)로 고르면 틀려요 — 번호가 작은 방이에요.
함정 넷: ②보다 ③을 먼저 하면, 방이 비는 바로 그 시각에 온 더 급한 환자가 밀려요.
함정 다섯: 기다린 시간의 합은 한 방에 10,000명이 몰리면 약 5×10^11이라 32비트 int를 넘어요(C·C++ long long, Java·C# long).
힙은 언어마다 달라요: Python heapq는 작은 것부터라 (-(s − a), 번호) 튜플을 넣고, Java PriorityQueue에 Comparator.comparingInt(...).thenComparingInt(...), C# PriorityQueue<TElement, TPriority>에 (-(s − a), 번호) 튜플 우선순위(작은 것부터), C++ std::priority_queue는 큰 것부터라 std::greater로 뒤집고, JavaScript·TypeScript·C는 힙이 없어서 직접 만들어요.
크레이콘도 힙을 직접 만들고, 목록 비교가 없어서 (1000000 − (s − a)) × 100000 + 번호처럼 수 하나로 묶어 넣어요 — 곱하는 수가 번호보다 작으면(× 1000) 9,999번 같은 번호가 앞 자리와 섞여요.
묶은 수는 2×10^11까지라 2^53 안이에요.

Pythonmain.py · 39 줄
import sysimport heapq data = sys.stdin.buffer.read().split()n, k = int(data[0]), int(data[1])arrive = [0] * nsev = [0] * ndur = [0] * nfor i in range(n):    arrive[i], sev[i], dur[i] = int(data[2 + 3 * i]), int(data[3 + 3 * i]), int(data[4 + 3 * i]) # 기다리는 사람의 급한 정도는 s + (T - a)인데, 모두 1분에 1씩 함께 오르니# 두 사람의 차례는 T와 상관없이 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람이 먼저waiting = []                  # (-(s - a), 번호)free = list(range(1, k + 1))  # 빈 진료실 번호(가장 작은 것이 맨 위)busy = []                     # (끝나는 시각, 진료실 번호)order = []seen = [0] * (k + 1)total_wait = 0nxt = 0  # 아직 오지 않은 첫 환자while len(order) < n:    # 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때    t = min(arrive[nxt] if nxt < n else float("inf"), busy[0][0] if busy else float("inf"))    while busy and busy[0][0] == t:          # ① 진료가 끝난 방부터 빈다        heapq.heappush(free, heapq.heappop(busy)[1])    while nxt < n and arrive[nxt] == t:      # ② 그 시각에 온 환자가 줄에 선다        heapq.heappush(waiting, (-(sev[nxt] - arrive[nxt]), nxt))        nxt += 1    while free and waiting:                  # ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다        room = heapq.heappop(free)        _, p = heapq.heappop(waiting)        order.append(p + 1)        seen[room] += 1        total_wait += t - arrive[p]        heapq.heappush(busy, (t + dur[p], room)) print(" ".join(map(str, order)))print(total_wait)print(" ".join(map(str, seen[1:])))
JavaScriptmain.js · 84 줄
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // 작은 것이 맨 위에 오는 이진 힙. less(x, y)가 참이면 x가 y보다 먼저 나온다class Heap {  constructor(less) {    this.a = [];    this.less = less;  }  get size() {    return this.a.length;  }  top() {    return this.a[0];  }  push(x) {    const a = this.a;    a.push(x);    let i = a.length - 1;    while (i > 0) {      const p = (i - 1) >> 1;      if (!this.less(a[i], a[p])) break;      [a[i], a[p]] = [a[p], a[i]];      i = p;    }  }  pop() {    const a = this.a;    const top = a[0];    const last = a.pop();    if (a.length > 0) {      a[0] = last;      let i = 0;      for (;;) {        const l = 2 * i + 1;        const r = l + 1;        let m = i;        if (l < a.length && this.less(a[l], a[m])) m = l;        if (r < a.length && this.less(a[r], a[m])) m = r;        if (m === i) break;        [a[i], a[m]] = [a[m], a[i]];        i = m;      }    }    return top;  }} const [n, k] = data;const arrive = [];const sev = [];const dur = [];for (let i = 0; i < n; i++) {  arrive.push(data[2 + 3 * i]);  sev.push(data[3 + 3 * i]);  dur.push(data[4 + 3 * i]);} // 급한 정도 s + (T - a)는 모두 1분에 1씩 함께 오르니 차례는 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람const key = (p) => sev[p] - arrive[p];const waiting = new Heap((x, y) => key(x) > key(y) || (key(x) === key(y) && x < y));const free = new Heap((x, y) => x < y); // 빈 진료실 번호const busy = new Heap((x, y) => x[0] < y[0] || (x[0] === y[0] && x[1] < y[1])); // [끝나는 시각, 진료실]for (let r = 1; r <= k; r++) free.push(r); const order = [];const seen = new Array(k + 1).fill(0);let totalWait = 0;let next = 0; // 아직 오지 않은 첫 환자while (order.length < n) {  // 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때  const t = Math.min(next < n ? arrive[next] : Infinity, busy.size > 0 ? busy.top()[0] : Infinity);  while (busy.size > 0 && busy.top()[0] === t) free.push(busy.pop()[1]); // ① 진료가 끝난 방부터 빈다  while (next < n && arrive[next] === t) waiting.push(next++); // ② 그 시각에 온 환자가 줄에 선다  while (free.size > 0 && waiting.size > 0) {    // ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다    const room = free.pop();    const p = waiting.pop();    order.push(p + 1);    seen[room]++;    totalWait += t - arrive[p];    busy.push([t + dur[p], room]);  }}console.log([order.join(" "), totalWait, seen.slice(1).join(" ")].join("\n"));
TypeScriptmain.ts · 85 줄
import { readFileSync } from "node:fs"; const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number); // 작은 것이 맨 위에 오는 이진 힙. less(x, y)가 참이면 x가 y보다 먼저 나온다class Heap<T> {  private readonly a: T[] = [];  constructor(private readonly less: (x: T, y: T) => boolean) {}  get size(): number {    return this.a.length;  }  top(): T {    return this.a[0];  }  push(x: T): void {    const a = this.a;    a.push(x);    let i = a.length - 1;    while (i > 0) {      const p = (i - 1) >> 1;      if (!this.less(a[i], a[p])) break;      [a[i], a[p]] = [a[p], a[i]];      i = p;    }  }  pop(): T {    const a = this.a;    const top = a[0];    const last = a.pop() as T;    if (a.length > 0) {      a[0] = last;      let i = 0;      for (;;) {        const l = 2 * i + 1;        const r = l + 1;        let m = i;        if (l < a.length && this.less(a[l], a[m])) m = l;        if (r < a.length && this.less(a[r], a[m])) m = r;        if (m === i) break;        [a[i], a[m]] = [a[m], a[i]];        i = m;      }    }    return top;  }} const [n, k] = data;const arrive: number[] = [];const sev: number[] = [];const dur: number[] = [];for (let i = 0; i < n; i++) {  arrive.push(data[2 + 3 * i]);  sev.push(data[3 + 3 * i]);  dur.push(data[4 + 3 * i]);} // 급한 정도 s + (T - a)는 모두 1분에 1씩 함께 오르니 차례는 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람const key = (p: number): number => sev[p] - arrive[p];const waiting = new Heap<number>((x, y) => key(x) > key(y) || (key(x) === key(y) && x < y));const free = new Heap<number>((x, y) => x < y); // 빈 진료실 번호type Busy = [end: number, room: number];const busy = new Heap<Busy>((x, y) => x[0] < y[0] || (x[0] === y[0] && x[1] < y[1]));for (let r = 1; r <= k; r++) free.push(r); const order: number[] = [];const seen: number[] = new Array<number>(k + 1).fill(0);let totalWait = 0;let next = 0; // 아직 오지 않은 첫 환자while (order.length < n) {  // 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때  const t = Math.min(next < n ? arrive[next] : Infinity, busy.size > 0 ? busy.top()[0] : Infinity);  while (busy.size > 0 && busy.top()[0] === t) free.push(busy.pop()[1]); // ① 진료가 끝난 방부터 빈다  while (next < n && arrive[next] === t) waiting.push(next++); // ② 그 시각에 온 환자가 줄에 선다  while (free.size > 0 && waiting.size > 0) {    // ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다    const room = free.pop();    const p = waiting.pop();    order.push(p + 1);    seen[room]++;    totalWait += t - arrive[p];    busy.push([t + dur[p], room]);  }}console.log([order.join(" "), totalWait, seen.slice(1).join(" ")].join("\n"));
JavaMain.java · 52 줄
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;        int[] arrive = new int[n], sev = new int[n], dur = new int[n];        for (int i = 0; i < n; i++) {            st.nextToken(); arrive[i] = (int) st.nval;            st.nextToken(); sev[i] = (int) st.nval;            st.nextToken(); dur[i] = (int) st.nval;        }         // 급한 정도 s + (T - a)는 모두 1분에 1씩 함께 오르니 차례는 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람        PriorityQueue<Integer> waiting = new PriorityQueue<>(                Comparator.comparingInt((Integer p) -> -(sev[p] - arrive[p])).thenComparingInt(p -> p));        PriorityQueue<Integer> free = new PriorityQueue<>(); // 빈 진료실 번호        // {끝나는 시각, 진료실}: 시각이 이르고, 같으면 방 번호가 작은 것이 먼저        PriorityQueue<int[]> busy = new PriorityQueue<>((x, y) -> x[0] != y[0] ? Integer.compare(x[0], y[0]) : Integer.compare(x[1], y[1]));        for (int r = 1; r <= k; r++) free.add(r);         StringBuilder order = new StringBuilder();        int[] seen = new int[k + 1];        long totalWait = 0; // 5×10^11까지 가서 int로는 넘친다        int called = 0, next = 0;        while (called < n) {            // 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때            int t = Integer.MAX_VALUE;            if (next < n) t = arrive[next];            if (!busy.isEmpty()) t = Math.min(t, busy.peek()[0]);            while (!busy.isEmpty() && busy.peek()[0] == t) free.add(busy.poll()[1]); // ① 진료가 끝난 방부터 빈다            while (next < n && arrive[next] == t) waiting.add(next++); // ② 그 시각에 온 환자가 줄에 선다            while (!free.isEmpty() && !waiting.isEmpty()) { // ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다                int room = free.poll();                int p = waiting.poll();                if (called++ > 0) order.append(' ');                order.append(p + 1);                seen[room]++;                totalWait += t - arrive[p];                busy.add(new int[] {t + dur[p], room});            }        }        StringBuilder sb = new StringBuilder();        sb.append(order).append('\n').append(totalWait).append('\n');        for (int r = 1; r <= k; r++) sb.append(seen[r]).append(r < k ? ' ' : '\n');        System.out.print(sb);    }}
C#Main.cs · 63 줄
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]);        var arrive = new int[n];        var sev = new int[n];        var dur = new int[n];        for (int i = 0; i < n; i++)        {            arrive[i] = int.Parse(data[2 + 3 * i]);            sev[i] = int.Parse(data[3 + 3 * i]);            dur[i] = int.Parse(data[4 + 3 * i]);        }         // 급한 정도 s + (T - a)는 모두 1분에 1씩 함께 오르니 차례는 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람        // PriorityQueue는 우선순위가 작은 것부터 꺼내니 (-(s - a), 번호)를 우선순위로 준다        var waiting = new PriorityQueue<int, (int, int)>();        var free = new PriorityQueue<int, int>();            // 빈 진료실 번호        var busy = new PriorityQueue<int, (int, int)>();     // 진료실, 우선순위 (끝나는 시각, 진료실)        for (int r = 1; r <= k; r++) free.Enqueue(r, r);         var order = new List<int>();        var seen = new int[k + 1];        long totalWait = 0; // 5×10^11까지 가서 int로는 넘친다        int next = 0;        while (order.Count < n)        {            // 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때            int t = int.MaxValue;            if (next < n) t = arrive[next];            if (busy.TryPeek(out _, out var b)) t = Math.Min(t, b.Item1);            while (busy.TryPeek(out int done, out var e) && e.Item1 == t) // ① 진료가 끝난 방부터 빈다            {                busy.Dequeue();                free.Enqueue(done, done);            }            while (next < n && arrive[next] == t) // ② 그 시각에 온 환자가 줄에 선다            {                waiting.Enqueue(next, (-(sev[next] - arrive[next]), next));                next++;            }            while (free.Count > 0 && waiting.Count > 0) // ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다            {                int room = free.Dequeue();                int p = waiting.Dequeue();                order.Add(p + 1);                seen[room]++;                totalWait += t - arrive[p];                busy.Enqueue(room, (t + dur[p], room));            }        }        var sb = new StringBuilder();        sb.Append(string.Join(' ', order)).Append('\n').Append(totalWait).Append('\n');        for (int r = 1; r <= k; r++) sb.Append(seen[r]).Append(r < k ? ' ' : '\n');        Console.Write(sb);    }}
C++main.cpp · 56 줄
#include <algorithm>#include <cstdint>#include <functional>#include <iostream>#include <queue>#include <utility>#include <vector> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n, k;    std::cin >> n >> k;    std::vector<int> arrive(n), sev(n), dur(n);    for (int i = 0; i < n; i++) std::cin >> arrive[i] >> sev[i] >> dur[i];     // priority_queue는 큰 것이 위다: std::greater를 주면 작은 것이 위    using P = std::pair<int, int>;    using MinHeap = std::priority_queue<P, std::vector<P>, std::greater<P>>;    // 급한 정도 s + (T - a)는 모두 1분에 1씩 함께 오르니 차례는 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람    MinHeap waiting;  // (-(s - a), 번호)    MinHeap busy;     // (끝나는 시각, 진료실)    std::priority_queue<int, std::vector<int>, std::greater<int>> freeRooms;  // 빈 진료실 번호    for (int r = 1; r <= k; r++) freeRooms.push(r);     std::vector<int> order;    std::vector<int> seen(k + 1, 0);    long long totalWait = 0;  // 5×10^11까지 가서 int로는 넘친다    int next = 0;    while (static_cast<int>(order.size()) < n) {        // 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때        int t = next < n ? arrive[next] : INT32_MAX;        if (!busy.empty()) t = std::min(t, busy.top().first);        while (!busy.empty() && busy.top().first == t) {  // ① 진료가 끝난 방부터 빈다            freeRooms.push(busy.top().second);            busy.pop();        }        while (next < n && arrive[next] == t) {  // ② 그 시각에 온 환자가 줄에 선다            waiting.push({-(sev[next] - arrive[next]), next});            next++;        }        while (!freeRooms.empty() && !waiting.empty()) {  // ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다            int room = freeRooms.top();            freeRooms.pop();            int p = waiting.top().second;            waiting.pop();            order.push_back(p + 1);            seen[room]++;            totalWait += t - arrive[p];            busy.push({t + dur[p], room});        }    }    for (int i = 0; i < n; i++) std::cout << order[i] << (i + 1 < n ? ' ' : '\n');    std::cout << totalWait << '\n';    for (int r = 1; r <= k; r++) std::cout << seen[r] << (r < k ? ' ' : '\n');}
Cmain.c · 78 줄
#include <stdio.h> #define MAXN 10000#define MAXK 100 /* 작은 것이 맨 위에 오는 이진 힙. 비교할 값을 long long 하나로 만들어 넣는다 */typedef struct {    long long a[MAXN + MAXK];    int size;} Heap; static void push(Heap *h, long long x) {    int i = h->size++;    h->a[i] = x;    while (i > 0) {        int p = (i - 1) / 2;        if (h->a[p] <= h->a[i]) break;        long long t = h->a[p];        h->a[p] = h->a[i];        h->a[i] = t;        i = p;    }} static long long pop(Heap *h) {    long long top = h->a[0];    h->a[0] = h->a[--h->size];    int i = 0;    for (;;) {        int l = 2 * i + 1, r = l + 1, m = i;        if (l < h->size && h->a[l] < h->a[m]) m = l;        if (r < h->size && h->a[r] < h->a[m]) m = r;        if (m == i) break;        long long t = h->a[m];        h->a[m] = h->a[i];        h->a[i] = t;        i = m;    }    return top;} static int arrive[MAXN], sev[MAXN], dur[MAXN], order[MAXN], seen[MAXK + 1];static Heap waiting, freeRooms, busy; int main(void) {    int n, k;    if (scanf("%d %d", &n, &k) != 2) return 0;    for (int i = 0; i < n; i++) scanf("%d %d %d", &arrive[i], &sev[i], &dur[i]);     /* 급한 정도 s + (T - a)는 모두 1분에 1씩 함께 오르니 차례는 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람.       s - a는 -10^6 ~ 10^6이라 (10^6 - (s - a)) * 100000 + 번호가 작을수록 먼저다 */    /* 바쁜 방은 끝나는 시각 * 1000 + 방 번호(끝나는 시각은 1.01×10^8까지) */    for (int r = 1; r <= k; r++) push(&freeRooms, r);    long long totalWait = 0; /* 5×10^11까지 가서 int로는 넘친다 */    int called = 0, next = 0;    while (called < n) {        /* 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때 */        long long t = next < n ? arrive[next] : -1;        if (busy.size > 0 && (t < 0 || busy.a[0] / 1000 < t)) t = busy.a[0] / 1000;        while (busy.size > 0 && busy.a[0] / 1000 == t) push(&freeRooms, pop(&busy) % 1000); /* ① 진료가 끝난 방부터 빈다 */        while (next < n && arrive[next] == t) { /* ② 그 시각에 온 환자가 줄에 선다 */            push(&waiting, (1000000LL - (sev[next] - arrive[next])) * 100000 + next);            next++;        }        while (freeRooms.size > 0 && waiting.size > 0) { /* ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다 */            int room = (int)pop(&freeRooms);            int p = (int)(pop(&waiting) % 100000);            order[called++] = p + 1;            seen[room]++;            totalWait += t - arrive[p];            push(&busy, (t + dur[p]) * 1000 + room);        }    }    for (int i = 0; i < n; i++) printf("%d%c", order[i], i + 1 < n ? ' ' : '\n');    printf("%lld\n", totalWait);    for (int r = 1; r <= k; r++) printf("%d%c", seen[r], r < k ? ' ' : '\n');    return 0;}
Craykonmain.cray · 121 줄
// 작은 수가 맨 위에 오는 최소 힙(목록 heap). heap[0]이 가장 작고, i의 자식은 2i+1, 2i+2힙넣기(heap, x){    heap.넣기(x);    i = heap.개수 - 1;    동안 (i > 0)    {        부모 = 수학.몫(i - 1, 2);        만약 (heap[부모] <= heap[i])        {            그만;        }        t = heap[부모];        heap[부모] = heap[i];        heap[i] = t;        i = 부모;    }} 힙빼기(heap){    top = heap[0];    heap[0] = heap[heap.개수 - 1];    heap.자리빼기(heap.개수 - 1);    i = 0;    동안 (참)    {        l = 2 * i + 1;        r = l + 1;        small = i;        만약 (l < heap.개수 && heap[l] < heap[small])        {            small = l;        }        만약 (r < heap.개수 && heap[r] < heap[small])        {            small = r;        }        만약 (small == i)        {            그만;        }        t = heap[small];        heap[small] = heap[i];        heap[i] = t;        i = small;    }    돌려주기 top;} 첫줄 = 콘솔.수들읽기();n = 첫줄[0];k = 첫줄[1];arrive = [];sev = [];dur = [];반복 (i = 0; i < n; i++){    p = 콘솔.수들읽기();    arrive.넣기(p[0]);    sev.넣기(p[1]);    dur.넣기(p[2]);} // 급한 정도 s + (T - a)는 모두 1분에 1씩 함께 오르니 차례는 s - a로 정해진다: 큰 것이 먼저, 같으면 번호가 작은 사람// 힙에는 수 하나만 넣는다: (1000000 - (s - a)) * 100000 + 번호 — 작을수록 먼저(2×10^11까지라 정확하다)waiting = [];// 바쁜 방: 끝나는 시각 * 1000 + 방 번호(끝나는 시각은 1.01×10^8까지)busy = [];free = []; // 빈 진료실 번호반복 (r = 1; r <= k; r++){    힙넣기(free, r);} order = [];seen = 새목록(k + 1, 0);totalWait = 0;next = 0; // 아직 오지 않은 첫 환자동안 (order.개수 < n){    // 다음에 무엇인가 바뀌는 시각: 다음 환자가 오거나 진료가 끝날 때    t = -1;    만약 (next < n)    {        t = arrive[next];    }    만약 (busy.개수 > 0)    {        끝 = 수학.몫(busy[0], 1000);        만약 (t < 0 || 끝 < t)        {            t = 끝;        }    }    // ① 진료가 끝난 방부터 빈다    동안 (busy.개수 > 0 && 수학.몫(busy[0], 1000) == t)    {        힙넣기(free, 힙빼기(busy) % 1000);    }    // ② 그 시각에 온 환자가 줄에 선다    동안 (next < n && arrive[next] == t)    {        힙넣기(waiting, (1000000 - (sev[next] - arrive[next])) * 100000 + next);        next += 1;    }    // ③ 번호가 작은 빈 방부터 가장 급한 사람을 부른다    동안 (free.개수 > 0 && waiting.개수 > 0)    {        room = 힙빼기(free);        p = 힙빼기(waiting) % 100000;        order.넣기(p + 1);        seen[room] += 1;        totalWait += t - arrive[p];        힙넣기(busy, (t + dur[p]) * 1000 + room);    }}콘솔.쓰기(order.잇기(" "));콘솔.쓰기(totalWait);seen.자리빼기(0);콘솔.쓰기(seen.잇기(" "));