{ }codelper
标准容器队列P0207

在车辆段连挂列车

在车辆段把车厢连成一排组成列车。
一开始一节车厢也没有。
按顺序处理 n 条命令。
车厢要在队列的前后两端挂上和摘下,所以使用标准库的双端队列(deque)容器。
- addf x:把编号为 x 的车厢挂在最前面。
- addb x:把编号为 x 的车厢挂在最后面。
- cutf k:摘下最前面的车厢,做 k 次。
剩下的车厢少于 k 节时,摘下所有车厢后停止。
按摘下的顺序(从最前面的车厢开始)在一行中输出摘下车厢的编号,用一个空格分隔。
没有摘下任何车厢时输出 -。
- cutb k:摘下最后面的车厢,做 k 次。
其余规则与 cutf 相同;因为按摘下的顺序,所以从最后面的车厢开始输出。
- rot k:把最前面的车厢摘下挂到最后面,做 k 次。
不输出任何内容。
没有车厢时什么也不做。
所有命令结束后,输出剩下的车厢数,下一行从前往后输出剩下车厢的编号,用一个空格分隔。
如果没有剩下车厢,第二行输出 -。

输入
第一行为 n(1 ≤ n ≤ 5000)。
接下来 n 行每行一条命令。
addf、addb 的 1 ≤ x ≤ 10^9,cutf、cutb 的 1 ≤ k ≤ 10^9,rot 的 0 ≤ k ≤ 10^9。
车厢编号可能重复。
输出
每个 cutf、cutb 输出一行(摘下车厢的编号,没有则为 -),然后是剩下的车厢数,然后是剩下车厢的编号(没有则为 -)
样例 1 · stdin
9
addb 1
addb 2
addf 3
rot 1
cutb 2
addb 9
addf 4
rot 4
cutf 1
stdout
3 2
1
2
9 4
样例 2 · stdin
5
cutf 3
addf 7
addf 8
cutb 5
rot 2
stdout
-
7 8
0
-
main.py
输入(stdin)

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

$ 就绪

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

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

本题只接受 Python、Java、C#、C++。
这些语言的标准库里有能在两端以 O(1) 放入和取出的容器。
JavaScript/TypeScript 数组的 unshift、shift 在前端操作时可能要移动所有元素,C 没有容器,Craykon 列表的 插入(0, x)、按位置移除(0) 也会把后面的元素逐个挪动。
各语言的名字不同:Python collections.deque 的 appendleft、append、popleft、pop、rotate,Java ArrayDeque 的 addFirst、addLast、pollFirst、pollLast,C++ std::deque 的 push_front、push_back、front+pop_front(pop_front() 不返回值)、std::rotate。
C# 没有 Deque,所以用 LinkedList<T> 的 AddFirst、AddLast、RemoveFirst、RemoveLast。
摘下的次数先减到 min(k, 剩余数),旋转只需做 k 除以车厢数的余数那么多次(转一圈等于没动);Python 的 rotate 会自动帮你约简。
五个陷阱:① Python rotate(k) 是向右转,后面的会到前面来。
要把前面的送到后面,用 rotate(-k)。
C++ std::rotate(begin, begin + r, end) 的中间参数也是"将成为第一个的元素",传 end - r 就会反着转。
② Python deque.pop() 从后面取出。
从前面取出是 popleft()。
③ Java ArrayDeque.push 像栈一样放在前面(addFirst)。
挂到后面是 addLast(add、offer)。
④ 不先约简就取 k 次,会从空容器里取:C# 因为 train.First 是 null 会以 NullReferenceException 停止,Java pop、removeFirst 抛出 NoSuchElementException,pollFirst 返回 null,放进 int 的那一刻就抛出 NullPointerException。
Python popleft 抛出 IndexError,C++ 的 front() 是未定义行为。
⑤ cutb 从最后面的车厢开始摘,所以按摘下的顺序写出时,与在列车上的顺序相反。

Pythonmain.py · 27 行
import sysfrom collections import deque lines = sys.stdin.read().split("\n")n = int(lines[0])train = deque()  # 앞뒤 어느 쪽에서도 넣고 빼는 데 O(1)out = []for cmd in lines[1:n + 1]:    op, arg = cmd.split()    k = int(arg)    if op == "addf":        train.appendleft(k)    elif op == "addb":        train.append(k)    elif op in ("cutf", "cutb"):        # 남은 차량보다 많이 떼라고 하면 있는 만큼만 뗀다(빈 deque에서 빼면 IndexError)        take = min(k, len(train))        if op == "cutf":            cut = [train.popleft() for _ in range(take)]        else:            cut = [train.pop() for _ in range(take)]  # 뗀 차례: 맨 뒤 차량부터        out.append(" ".join(map(str, cut)) if cut else "-")    elif op == "rot":        train.rotate(-k)  # 음수는 왼쪽으로: 앞의 것이 뒤로 간다(빈 deque는 그대로)out.append(str(len(train)))out.append(" ".join(map(str, train)) if train else "-")print("\n".join(out))
JavaMain.java · 44 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayDeque;import java.util.StringJoiner; 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());        ArrayDeque<Integer> train = new ArrayDeque<>(); // 앞뒤 어느 쪽에서도 넣고 빼는 데 O(1)        StringBuilder out = new StringBuilder();        for (int i = 0; i < n; i++) {            String[] p = in.readLine().trim().split(" ");            long k = Long.parseLong(p[1]);            switch (p[0]) {                case "addf" -> train.addFirst((int) k);                case "addb" -> train.addLast((int) k);                case "cutf", "cutb" -> {                    // 있는 만큼만 뗀다(빈 ArrayDeque에서 removeFirst·pop은 예외를 던진다)                    long take = Math.min(k, train.size());                    StringJoiner cut = new StringJoiner(" ");                    for (long r = 0; r < take; r++) {                        int car = p[0].equals("cutf") ? train.pollFirst() : train.pollLast(); // 뒤는 맨 뒤 차량부터                        cut.add(String.valueOf(car));                    }                    out.append(take == 0 ? "-" : cut.toString()).append('\n');                }                case "rot" -> {                    if (!train.isEmpty()) {                        long r = k % train.size(); // 한 바퀴 돌면 제자리다                        for (long s = 0; s < r; s++) train.addLast(train.pollFirst());                    }                }                default -> throw new IllegalArgumentException(p[0]);            }        }        out.append(train.size()).append('\n');        StringJoiner items = new StringJoiner(" ");        for (int x : train) items.add(String.valueOf(x)); // 앞에서 뒤로 돈다        out.append(train.isEmpty() ? "-" : items.toString()).append('\n');        System.out.print(out);    }}
C#Main.cs · 51 行
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine()!.Trim());        // .NET에는 Deque가 없다. LinkedList<T>는 앞뒤에 넣고 빼는 데 O(1)이다        var train = new LinkedList<int>();        var output = new StringBuilder();        for (int i = 0; i < n; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            long k = long.Parse(p[1]);            switch (p[0])            {                case "addf": train.AddFirst((int)k); break;                case "addb": train.AddLast((int)k); break;                case "cutf":                case "cutb":                    // 있는 만큼만 뗀다(비었는데 First를 읽으면 null이다)                    long take = Math.Min(k, train.Count);                    var cut = new List<int>();                    for (long r = 0; r < take; r++)                    {                        if (p[0] == "cutf") { cut.Add(train.First!.Value); train.RemoveFirst(); }                        else { cut.Add(train.Last!.Value); train.RemoveLast(); } // 맨 뒤 차량부터                    }                    output.Append(cut.Count == 0 ? "-" : string.Join(" ", cut)).Append('\n');                    break;                case "rot":                    if (train.Count > 0)                    {                        long r = k % train.Count; // 한 바퀴 돌면 제자리다                        for (long s = 0; s < r; s++)                        {                            var node = train.First!;                            train.RemoveFirst();                            train.AddLast(node); // 마디를 그대로 옮긴다                        }                    }                    break;            }        }        output.Append(train.Count).Append('\n');        output.Append(train.Count == 0 ? "-" : string.Join(" ", train)).Append('\n');        Console.Write(output);    }}
C++main.cpp · 45 行
#include <algorithm>#include <deque>#include <iostream>#include <string> int main() {    std::ios::sync_with_stdio(false);    int n;    std::cin >> n;    std::deque<int> train; // 앞뒤 어느 쪽에서도 넣고 빼는 데 O(1)    for (int i = 0; i < n; i++) {        std::string op;        long long k;        std::cin >> op >> k;        if (op == "addf") {            train.push_front((int)k);        } else if (op == "addb") {            train.push_back((int)k);        } else if (op == "cutf" || op == "cutb") {            // 빈 deque에서 front()·pop_front()는 정의되지 않은 동작이라, 있는 만큼만 뗀다            long long take = std::min(k, (long long)train.size());            if (take == 0) std::cout << '-';            for (long long r = 0; r < take; r++) {                if (r > 0) std::cout << ' ';                if (op == "cutf") {                    std::cout << train.front(); // pop_front()는 값을 돌려주지 않는다                    train.pop_front();                } else {                    std::cout << train.back(); // 뗀 차례: 맨 뒤 차량부터                    train.pop_back();                }            }            std::cout << '\n';        } else if (op == "rot") {            if (!train.empty()) {                long long r = k % (long long)train.size(); // 한 바퀴 돌면 제자리다                // [begin, begin + r)이 끝으로 간다: 앞의 r개가 뒤로                std::rotate(train.begin(), train.begin() + r, train.end());            }        }    }    std::cout << train.size() << '\n';    if (train.empty()) std::cout << "-\n";    for (std::size_t i = 0; i < train.size(); i++) std::cout << train[i] << (i + 1 < train.size() ? ' ' : '\n');}