One CPU runs n processes in turns. Time is an integer starting at 0.
Process i arrives at time aᵢ and is finished after running for bᵢ in total.
Arrival times satisfy a₁ ≤ a₂ ≤ … ≤ aₙ.
An arriving process joins the end of the waiting line, and processes arriving at the same time line up with the smaller number in front.
The CPU repeats the following rules until every process is finished.
1. If the line is empty, it idles until the next process arrives, and the processes arriving at that time join the line.
2. It takes the process at the front of the line and runs it for the smaller of its remaining work and the time slice Q. The run goes from the current time t to the time t′ that much later.
3. Processes that arrived by time t′ (including t′) and have not joined the line yet join it first.
4. Then, if the process just run still has work left it joins the end of the line; if it is done, its finish time is t′.
Print each process's finish time in process-number order on one line, separated by spaces.
The first line gives n and Q, and each of the next n lines gives aᵢ and bᵢ separated by a space.
Input
1 ≤ n ≤ 5,000, 1 ≤ Q ≤ 10⁹, 0 ≤ a₁ ≤ … ≤ aₙ ≤ 10⁹, 1 ≤ bᵢ ≤ 10⁹, bᵢ ≤ 50 × Q (each process gets at most 50 turns)
Output
n finish times (one line, separated by spaces)
Example 1 · stdin
3 2
0 5
1 3
2 1
stdout
9 8 5
Example 2 · stdin
2 3
0 4
10 2
stdout
4 12
main.py
Error List00F8 ↓ · ⇧F8 ↑
Input (stdin)
When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).
$ Ready
Open this on a computer to write and run code. On mobile you can read the problem and the solutions.
In other languagesShow solutions by language · 8 languages
This is a simulation that follows the rules as written, and the waiting line is a queue: take from the front, add to the back. There are at most 50 × 5,000 = 250,000 turns, so jump a whole slice at a time instead of adding 1 to the clock, and it finishes quickly. Using a queue: Python collections.deque with popleft(), Java ArrayDeque with pollFirst()/addLast(), C# Queue<int> with Dequeue()/Enqueue(), C++ std::queue with front()/pop()/push() (pop() returns nothing, so read front() first). JavaScript/TypeScript have no queue, and shift() is tempting but shifts everything behind it, so only push to the array and move a front index head. C and Craykon use a circular queue of size n (insert at (head + size) % n) — the line never holds more than n. Craykon makes the slots with NewList(n, 0), and its function changes the outer variables size and next directly. Four traps: (1) order at the same time — processes arriving at the slice's end time t′ line up before the process just run (rule 3, then 4). Swapping them makes example 1 something other than 9 8 5. (2) When the line is empty the clock must move to the next arrival. Otherwise the second process of example 2 runs early at time 4 and gives 6. (3) Finish times reach 5×10¹², so Java, C#, C and C++ need long/long long (the doubles of JavaScript, TypeScript and Craykon are exact up to 2^53, and Python does not overflow). (4) Taking from the back (a stack) runs late arrivals first.
Pythonmain.py · 38 lines
import sysfrom collections import dequedata = list(map(int, sys.stdin.read().split()))n, q = data[0], data[1]arrive = data[2::2]left = data[3::2] # 남은 일finish = [0] * nline = deque()t = 0nxt = 0 # 아직 줄에 서지 않은 첫 프로세스done = 0def join_until(now): """now까지 도착한 프로세스를 번호 차례로 줄에 세운다.""" global nxt while nxt < n and arrive[nxt] <= now: line.append(nxt) nxt += 1while done < n: if not line: # 줄이 비면 다음 프로세스가 올 때까지 쉰다 t = max(t, arrive[nxt]) join_until(t) p = line.popleft() run = min(q, left[p]) t += run left[p] -= run join_until(t) # 이 시각까지 온 프로세스가 먼저 줄에 서고 if left[p] > 0: line.append(p) # 그 뒤에 방금 돈 프로세스가 선다 else: finish[p] = t done += 1print(" ".join(map(str, finish)))
JavaScriptmain.js · 40 lines
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, q] = data;const arrive = [];const left = []; // 남은 일for (let i = 0; i < n; i++) { arrive.push(data[2 + 2 * i]); left.push(data[3 + 2 * i]);}// 배열 + 앞 자리(head)로 큐를 만든다. shift()는 뒤의 것을 모두 당긴다.const line = [];let head = 0;const finish = new Array(n).fill(0);let t = 0;let next = 0; // 아직 줄에 서지 않은 첫 프로세스let done = 0;// now까지 도착한 프로세스를 번호 차례로 줄에 세운다function joinUntil(now) { while (next < n && arrive[next] <= now) line.push(next++);}while (done < n) { if (head === line.length) { // 줄이 비면 다음 프로세스가 올 때까지 쉰다 t = Math.max(t, arrive[next]); joinUntil(t); } const p = line[head++]; const run = Math.min(q, left[p]); t += run; left[p] -= run; joinUntil(t); // 이 시각까지 온 프로세스가 먼저 줄에 서고 if (left[p] > 0) line.push(p); // 그 뒤에 방금 돈 프로세스가 선다 else { finish[p] = t; done++; }}console.log(finish.join(" "));
TypeScriptmain.ts · 42 lines
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [n, q] = data;const arrive: number[] = [];const left: number[] = []; // 남은 일for (let i = 0; i < n; i++) { arrive.push(data[2 + 2 * i]); left.push(data[3 + 2 * i]);}// 배열 + 앞 자리(head)로 큐를 만든다. shift()는 뒤의 것을 모두 당긴다.const line: number[] = [];let head = 0;const finish: number[] = new Array<number>(n).fill(0);let t = 0;let next = 0; // 아직 줄에 서지 않은 첫 프로세스let done = 0;// now까지 도착한 프로세스를 번호 차례로 줄에 세운다function joinUntil(now: number): void { while (next < n && arrive[next] <= now) line.push(next++);}while (done < n) { if (head === line.length) { // 줄이 비면 다음 프로세스가 올 때까지 쉰다 t = Math.max(t, arrive[next]); joinUntil(t); } const p = line[head++]; const run = Math.min(q, left[p]); t += run; left[p] -= run; joinUntil(t); // 이 시각까지 온 프로세스가 먼저 줄에 서고 if (left[p] > 0) line.push(p); // 그 뒤에 방금 돈 프로세스가 선다 else { finish[p] = t; done++; }}console.log(finish.join(" "));
JavaMain.java · 53 lines
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayDeque;import java.util.StringTokenizer;public class Main { static int n, next = 0; // next: 아직 줄에 서지 않은 첫 프로세스 static long[] arrive; static ArrayDeque<Integer> line = new ArrayDeque<>(); // now까지 도착한 프로세스를 번호 차례로 줄에 세운다 static void joinUntil(long now) { while (next < n && arrive[next] <= now) line.addLast(next++); } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(in.readLine()); n = Integer.parseInt(st.nextToken()); long q = Long.parseLong(st.nextToken()); arrive = new long[n]; long[] left = new long[n]; // 남은 일 for (int i = 0; i < n; i++) { st = new StringTokenizer(in.readLine()); arrive[i] = Long.parseLong(st.nextToken()); left[i] = Long.parseLong(st.nextToken()); } long[] finish = new long[n]; long t = 0; // 5×10¹²까지 가서 long int done = 0; while (done < n) { if (line.isEmpty()) { // 줄이 비면 다음 프로세스가 올 때까지 쉰다 t = Math.max(t, arrive[next]); joinUntil(t); } int p = line.pollFirst(); long run = Math.min(q, left[p]); t += run; left[p] -= run; joinUntil(t); // 이 시각까지 온 프로세스가 먼저 줄에 서고 if (left[p] > 0) line.addLast(p); // 그 뒤에 방금 돈 프로세스가 선다 else { finish[p] = t; done++; } } StringBuilder out = new StringBuilder(); for (int i = 0; i < n; i++) out.append(i > 0 ? " " : "").append(finish[i]); System.out.println(out); }}
C#Main.cs · 52 lines
using System;using System.Collections.Generic;class Program{ static void Main() { string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); int n = int.Parse(first[0]); long q = long.Parse(first[1]); long[] arrive = new long[n]; long[] left = new long[n]; // 남은 일 for (int i = 0; i < n; i++) { string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); arrive[i] = long.Parse(p[0]); left[i] = long.Parse(p[1]); } var line = new Queue<int>(); int next = 0; // 아직 줄에 서지 않은 첫 프로세스 // now까지 도착한 프로세스를 번호 차례로 줄에 세운다 void JoinUntil(long now) { while (next < n && arrive[next] <= now) line.Enqueue(next++); } long[] finish = new long[n]; long t = 0; // 5×10¹²까지 가서 long int done = 0; while (done < n) { if (line.Count == 0) // 줄이 비면 다음 프로세스가 올 때까지 쉰다 { t = Math.Max(t, arrive[next]); JoinUntil(t); } int p = line.Dequeue(); long run = Math.Min(q, left[p]); t += run; left[p] -= run; JoinUntil(t); // 이 시각까지 온 프로세스가 먼저 줄에 서고 if (left[p] > 0) line.Enqueue(p); // 그 뒤에 방금 돈 프로세스가 선다 else { finish[p] = t; done++; } } Console.WriteLine(string.Join(" ", finish)); }}
C++main.cpp · 42 lines
#include <algorithm>#include <iostream>#include <queue>#include <vector>int main() { int n; long long q; std::cin >> n >> q; std::vector<long long> arrive(n), left(n); // left: 남은 일 for (int i = 0; i < n; i++) std::cin >> arrive[i] >> left[i]; std::queue<int> line; int next = 0; // 아직 줄에 서지 않은 첫 프로세스 // now까지 도착한 프로세스를 번호 차례로 줄에 세운다 auto joinUntil = [&](long long now) { while (next < n && arrive[next] <= now) line.push(next++); }; std::vector<long long> finish(n); long long t = 0; // 5×10¹²까지 가서 long long int done = 0; while (done < n) { if (line.empty()) { // 줄이 비면 다음 프로세스가 올 때까지 쉰다 t = std::max(t, arrive[next]); joinUntil(t); } int p = line.front(); // pop()은 값을 돌려주지 않아서 front()를 먼저 읽는다 line.pop(); long long run = std::min(q, left[p]); t += run; left[p] -= run; joinUntil(t); // 이 시각까지 온 프로세스가 먼저 줄에 서고 if (left[p] > 0) line.push(p); // 그 뒤에 방금 돈 프로세스가 선다 else { finish[p] = t; done++; } } for (int i = 0; i < n; i++) std::cout << (i ? " " : "") << finish[i]; std::cout << "\n";}
Cmain.c · 55 lines
#include <stdio.h>#define MAXN 5000static long long arrive[MAXN], left[MAXN], finish[MAXN];/* 크기 MAXN인 원형 큐: 줄에는 많아야 n개만 있다 */static int line[MAXN];static int head = 0, size = 0;static int n, next = 0; /* next: 아직 줄에 서지 않은 첫 프로세스 */static void push(int p) { line[(head + size) % MAXN] = p; size++;}static int pop(void) { int p = line[head]; head = (head + 1) % MAXN; size--; return p;}/* now까지 도착한 프로세스를 번호 차례로 줄에 세운다 */static void join_until(long long now) { while (next < n && arrive[next] <= now) push(next++);}int main(void) { long long q; scanf("%d %lld", &n, &q); for (int i = 0; i < n; i++) scanf("%lld %lld", &arrive[i], &left[i]); long long t = 0; /* 5×10¹²까지 가서 long long */ int done = 0; while (done < n) { if (size == 0) { /* 줄이 비면 다음 프로세스가 올 때까지 쉰다 */ if (arrive[next] > t) t = arrive[next]; join_until(t); } int p = pop(); long long run = left[p] < q ? left[p] : q; t += run; left[p] -= run; join_until(t); /* 이 시각까지 온 프로세스가 먼저 줄에 서고 */ if (left[p] > 0) { push(p); /* 그 뒤에 방금 돈 프로세스가 선다 */ } else { finish[p] = t; done++; } } for (int i = 0; i < n; i++) printf(i ? " %lld" : "%lld", finish[i]); putchar('\n'); return 0;}
Craykonmain.cray · 62 lines
첫줄 = Console.ReadNumbers();n = 첫줄[0];q = 첫줄[1];arrive = NewList(n, 0);left = NewList(n, 0);for (i = 0; i < n; i++){ 줄 = Console.ReadNumbers(); arrive[i] = 줄[0]; left[i] = 줄[1];}// 원형 큐: 줄에는 많아야 n개라 n칸이면 된다. head는 맨 앞 자리, size는 줄 길이.line = NewList(n, 0);head = 0;size = 0;// 아직 줄에 서지 않은 첫 프로세스next = 0;// now까지 도착한 프로세스를 번호 차례로 줄에 세운다줄세우기(now){ while (next < n && arrive[next] <= now) { line[(head + size) % n] = next; size += 1; next += 1; }}finish = NewList(n, 0);t = 0;done = 0;while (done < n){ if (size == 0) { // 줄이 비면 다음 프로세스가 올 때까지 쉰다 t = Mathf.Max(t, arrive[next]); 줄세우기(t); } p = line[head]; head = (head + 1) % n; size -= 1; run = Mathf.Min(q, left[p]); t += run; left[p] -= run; // 이 시각까지 온 프로세스가 먼저 줄에 서고 줄세우기(t); if (left[p] > 0) { // 그 뒤에 방금 돈 프로세스가 선다 line[(head + size) % n] = p; size += 1; } else { finish[p] = t; done += 1; }}Console.WriteLine(finish);