{ }codelper
模拟取余P0114

红绿灯前的车

一条单行道上有一个红绿灯。
时刻是 0, 1, 2, … 这样以秒为单位的整数。
信号从时刻 0 开始,依次重复绿灯 G 秒、红灯 R 秒。
也就是说,时刻 t 除以 G + R 的余数小于 G 时为绿灯,否则为红灯。
共有 n 辆车,第 i 辆车在时刻 aᵢ 到达红绿灯前,排到队尾。
到达时刻满足 a₁ ≤ a₂ ≤ … ≤ aₙ,同一时刻到达的车,编号小的排在前面。
每个绿灯时刻只有队首的一辆车通过,红灯时刻谁也不能通过。
如果队伍为空,车可以在到达的那一刻就通过。
第一行按编号顺序输出每辆车通过的时刻,用空格隔开。
第二行输出等得最久的车的编号和等待时间(通过时刻 − 到达时刻),用空格隔开。
如果等得最久的车有多辆,输出编号最小的那辆。
第一行给出 n、G、R,第二行给出用空格隔开的 a₁ … aₙ。

输入
1 ≤ n ≤ 5,000,1 ≤ G, R ≤ 10⁹,0 ≤ a₁ ≤ … ≤ aₙ ≤ 10⁹
输出
两行:第一行是 n 个通过时刻,第二行是车的编号和等待时间
样例 1 · stdin
6 3 2
0 0 1 4 4 9
stdout
0 1 2 5 6 10
5 2
样例 2 · stdin
1 1 1000000000
1
stdout
1000000001
1 1000000000
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

一秒一秒地走时钟来模拟,红灯长达 10 亿秒时要循环好几万亿次。
车只按排队顺序通过,所以直接求每辆车「最早能通过的时刻」:取到达时刻和前车通过时刻 + 1 中较晚的作为 t,若 m = t % (G + R) 不小于 G(红灯),就给 t 加上 (G + R) − m,跳到下一个周期的开头。
四个陷阱:① 余数恰好为 G 的时刻已经是红灯——写成 m > G 会把这一秒当成绿灯。
② 不能和前车在同一秒通过,所以是 prev + 1(写成 prev 会一秒过两辆)。
③ 周期 G + R 可达 2×10⁹,放不进 int(约 21 亿),通过时刻可达 5×10¹²——Java、C#、C、C++ 要用 long/long long 读入。
Python 不会溢出,JavaScript、TypeScript、Craykon 的数是 double,在 2^53 以内是精确的。
④ 等待最久的车有多辆时取编号小的,所以用 > 比较(>= 会选到后面的车)。
取余 % 在八种语言里都是同一个符号,这里没有负数,所以不用担心各语言不同的负数取余规则。
Craykon 在一行里写两个值时,用 (best + 1) + " " + … 这样拼接字符串。

Pythonmain.py · 20 行
n, g, r = map(int, input().split())a = list(map(int, input().split()))period = g + r passed = []prev = -1  # 앞 차가 지나간 시각(아직 없으면 -1)for arrive in a:    t = max(arrive, prev + 1)  # 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터    m = t % period    if m >= g:  # 빨강이면 다음 초록(다음 주기의 처음)까지 기다린다        t += period - m    passed.append(t)    prev = t best = 0  # 가장 오래 기다린 차의 자리(같으면 앞 번호)for i in range(n):    if passed[i] - a[i] > passed[best] - a[best]:        best = iprint(" ".join(map(str, passed)))print(best + 1, passed[best] - a[best])
JavaScriptmain.js · 19 行
const [n, g, r, ...a] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const period = g + r; const passed = [];let prev = -1; // 앞 차가 지나간 시각(아직 없으면 -1)for (const arrive of a) {  let t = Math.max(arrive, prev + 1); // 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터  const m = t % period;  if (m >= g) t += period - m; // 빨강이면 다음 주기의 처음까지 기다린다  passed.push(t);  prev = t;} let best = 0; // 가장 오래 기다린 차의 자리(같으면 앞 번호)for (let i = 0; i < n; i++) {  if (passed[i] - a[i] > passed[best] - a[best]) best = i;}console.log(passed.join(" "));console.log(`${best + 1} ${passed[best] - a[best]}`);
TypeScriptmain.ts · 21 行
import { readFileSync } from "node:fs"; const [n, g, r, ...a]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const period = g + r; const passed: number[] = [];let prev = -1; // 앞 차가 지나간 시각(아직 없으면 -1)for (const arrive of a) {  let t = Math.max(arrive, prev + 1); // 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터  const m = t % period;  if (m >= g) t += period - m; // 빨강이면 다음 주기의 처음까지 기다린다  passed.push(t);  prev = t;} let best = 0; // 가장 오래 기다린 차의 자리(같으면 앞 번호)for (let i = 0; i < n; i++) {  if (passed[i] - a[i] > passed[best] - a[best]) best = i;}console.log(passed.join(" "));console.log(`${best + 1} ${passed[best] - a[best]}`);
JavaMain.java · 38 行
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));        StringTokenizer st = new StringTokenizer(in.readLine());        int n = Integer.parseInt(st.nextToken());        long g = Long.parseLong(st.nextToken());        long r = Long.parseLong(st.nextToken());        long period = g + r; // 2×10⁹까지라 int를 넘는다        long[] a = new long[n];        st = new StringTokenizer(in.readLine());        for (int i = 0; i < n; i++) a[i] = Long.parseLong(st.nextToken());         long[] passed = new long[n];        long prev = -1; // 앞 차가 지나간 시각(아직 없으면 -1)        for (int i = 0; i < n; i++) {            long t = Math.max(a[i], prev + 1); // 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터            long m = t % period;            if (m >= g) t += period - m; // 빨강이면 다음 주기의 처음까지 기다린다            passed[i] = t;            prev = t;        }         int best = 0; // 가장 오래 기다린 차의 자리(같으면 앞 번호)        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            if (passed[i] - a[i] > passed[best] - a[best]) best = i;            if (i > 0) out.append(' ');            out.append(passed[i]);        }        out.append('\n').append(best + 1).append(' ').append(passed[best] - a[best]);        System.out.println(out);    }}
C#Main.cs · 33 行
using System;using System.Linq; class Program{    static void Main()    {        long[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(long.Parse).ToArray();        int n = (int)first[0];        long g = first[1], r = first[2];        long period = g + r; // 2×10⁹까지라 int를 넘는다        long[] a = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(long.Parse).ToArray();         long[] passed = new long[n];        long prev = -1; // 앞 차가 지나간 시각(아직 없으면 -1)        for (int i = 0; i < n; i++)        {            long t = Math.Max(a[i], prev + 1); // 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터            long m = t % period;            if (m >= g) t += period - m; // 빨강이면 다음 주기의 처음까지 기다린다            passed[i] = t;            prev = t;        }         int best = 0; // 가장 오래 기다린 차의 자리(같으면 앞 번호)        for (int i = 0; i < n; i++)        {            if (passed[i] - a[i] > passed[best] - a[best]) best = i;        }        Console.WriteLine(string.Join(" ", passed));        Console.WriteLine($"{best + 1} {passed[best] - a[best]}");    }}
C++main.cpp · 28 行
#include <algorithm>#include <iostream>#include <vector> int main() {    int n;    long long g, r;    std::cin >> n >> g >> r;    const long long period = g + r; // 2×10⁹까지라 int를 넘는다    std::vector<long long> a(n), passed(n);    for (long long& x : a) std::cin >> x;     long long prev = -1; // 앞 차가 지나간 시각(아직 없으면 -1)    for (int i = 0; i < n; i++) {        long long t = std::max(a[i], prev + 1); // 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터        long long m = t % period;        if (m >= g) t += period - m; // 빨강이면 다음 주기의 처음까지 기다린다        passed[i] = t;        prev = t;    }     int best = 0; // 가장 오래 기다린 차의 자리(같으면 앞 번호)    for (int i = 0; i < n; i++) {        if (passed[i] - a[i] > passed[best] - a[best]) best = i;        std::cout << (i ? " " : "") << passed[i];    }    std::cout << "\n" << best + 1 << " " << passed[best] - a[best] << "\n";}
Cmain.c · 28 行
#include <stdio.h> static long long a[5000], passed[5000]; int main(void) {    int n;    long long g, r;    scanf("%d %lld %lld", &n, &g, &r);    long long period = g + r; /* 2×10⁹까지라 int를 넘는다 */    for (int i = 0; i < n; i++) scanf("%lld", &a[i]);     long long prev = -1; /* 앞 차가 지나간 시각(아직 없으면 -1) */    for (int i = 0; i < n; i++) {        long long t = a[i] > prev + 1 ? a[i] : prev + 1; /* 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터 */        long long m = t % period;        if (m >= g) t += period - m; /* 빨강이면 다음 주기의 처음까지 기다린다 */        passed[i] = t;        prev = t;    }     int best = 0; /* 가장 오래 기다린 차의 자리(같으면 앞 번호) */    for (int i = 0; i < n; i++) {        if (passed[i] - a[i] > passed[best] - a[best]) best = i;        printf(i ? " %lld" : "%lld", passed[i]);    }    printf("\n%d %lld\n", best + 1, passed[best] - a[best]);    return 0;}
Craykonmain.cray · 35 行
첫줄 = 控制台.读数字();n = 첫줄[0];g = 첫줄[1];r = 첫줄[2];a = 控制台.读数字();period = g + r; passed = [];// 앞 차가 지나간 시각(아직 없으면 -1)prev = -1;遍历 (arrive 在 a){    // 도착한 뒤, 그리고 앞 차가 지나간 다음 초부터    t = 数学.较大值(arrive, prev + 1);    m = t % period;    如果 (m >= g)    {        // 빨강이면 다음 주기의 처음까지 기다린다        t += period - m;    }    passed.添加(t);    prev = t;} // 가장 오래 기다린 차의 자리(같으면 앞 번호)best = 0;循环 (i = 0; i < n; i++){    如果 (passed[i] - a[i] > passed[best] - a[best])    {        best = i;    }}控制台.输出(passed);控制台.输出(best + 1 + " " + (passed[best] - a[best]));