{ }codelper
方法类P0323

一起流动的沙漏

有 n 个沙漏,第 i 个沙漏里有 c_i 粒沙。
一开始每个沙漏的沙全部在下半部。
时间流逝时,上半部有沙的沙漏每秒有一粒沙从上半部落到下半部,上半部空了之后这个沙漏就不再流动。
把沙漏写成一个类,并把“翻转”“流动 t 秒”“询问上半部是否为空”“询问上半部剩余的沙数”写成方法,依次处理下面的 q 条命令。
flip i:把第 i 个沙漏翻转(上半部和下半部的沙数互换)。
不输出任何内容。
wait t:所有沙漏都经过 t 秒。
输出在这 t 秒内上半部变空的沙漏个数。
命令之前上半部就已经为空的沙漏不计入。
until i:所有沙漏同样经过若干秒,秒数等于命令执行前第 i 个沙漏上半部剩余的沙数(如果第 i 个沙漏的上半部已经为空,则时间不流逝)。
然后输出从最开始到现在通过 wait 和 until 流逝的时间总秒数。
flipdone:把上半部为空的沙漏全部翻转,并输出翻转的沙漏个数。
处理完所有命令后,从第 1 个到第 n 个,对每个沙漏输出一行:上半部的沙数和下半部的沙数,用一个空格隔开。

输入
第一行给出沙漏数 n 和命令数 q,用一个空格隔开。
第二行给出 c_1, …, c_n,用一个空格隔开。
接下来 q 行每行给出一条命令。
1 ≤ n ≤ 100,1 ≤ q ≤ 1,000,1 ≤ c_i ≤ 1,000,000,1 ≤ t ≤ 1,000,000,1 ≤ i ≤ n
输出
对每条 wait、until、flipdone 命令输出一行答案(只输出数字,不带逗号)。
之后输出 n 行,每行是一个沙漏上半部和下半部的沙数,用一个空格隔开
样例 1 · stdin
2 7
4 7
flipdone
until 1
flipdone
until 2
wait 5
flipdone
flip 2
stdout
2
4
1
7
1
2
4 0
0 7
main.py
输入(stdin)

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

$ 就绪

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

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

这道题只接受有类的 Python、JavaScript、TypeScript、Java、C#、C++(C 没有类,Craykon 目前还不能定义类)。
方法是读取或修改对象内部的值的函数。
Python 要像 def run(self, seconds): 这样自己写出第一个参数 self,JS、TS 在 run(seconds) { … } 里用 this.top 访问字段。
Java、C#、C++ 直接写 top 就是字段。
C++ 在不修改值的方法后面加 const(bool isEmpty() const)。
翻转就是交换两个字段:Python self.top, self.bottom = self.bottom, self.top,JS、TS [this.top, this.bottom] = [this.bottom, this.top],C# (top, bottom) = (bottom, top),C++ std::swap(top_, bottom_) 都是一行,Java 则用一个临时变量。
六个陷阱:① 像 top = bottom; bottom = top; 这样依次赋值,上半部的值会先丢失,两部分变成同一个数。
② 调用方法时漏掉括号。
Python 的 if g.is_empty: 和 JavaScript 的 if (g.isEmpty) 不报错也能运行,但方法本身永远为真,于是连不空的沙漏也被翻转(示例中第二个 flipdone 变成 2 而不是 1)。
TypeScript 会报“这个条件永远为真”的错误(TS2774),Java、C#、C++ 则报编译错误。
想不带括号使用,可以写成 Python @property、JS/TS get isEmpty()、C# 属性 bool IsEmpty => top == 0;。
③ 流动时只能移动 min(seconds, top) 粒。
直接写 top -= seconds 会让上半部变成负数。
④“这次变空了”和“现在是空的”不一样。
如果流动方法返回 top == 0,连原本就空的沙漏也会被计入(示例中的 wait 5 变成 2 而不是 1)。
⑤ until 中要流动的时间在循环之前只问一次并存进变量。
在循环里像 g.run(target.remaining()) 这样重新询问,第 i 个沙漏变空之后就得到 0,编号在它后面的沙漏不会流动。
⑥ C++ 的 for (Hourglass g : glasses) 会复制沙漏,时间流在副本上。
写成 Hourglass& g 才会改变真正的沙漏。
Python、JS、TS、Java、C# 的循环变量指向同一个对象,所以会直接改变。

Pythonmain.py · 65 行
import sys  class Hourglass:    def __init__(self, capacity):        # 처음에는 모래가 전부 아래 칸에 있다        self.top = 0        self.bottom = capacity     def flip(self):        self.top, self.bottom = self.bottom, self.top     def run(self, seconds):        """seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 True를 돌려준다."""        moved = min(seconds, self.top)        self.top -= moved        self.bottom += moved        return moved > 0 and self.top == 0     def is_empty(self):        return self.top == 0     def remaining(self):        return self.top  def main():    lines = sys.stdin.read().split("\n")    n, q = map(int, lines[0].split())    glasses = [Hourglass(int(c)) for c in lines[1].split()]    elapsed = 0    out = []    for line in lines[2:2 + q]:        parts = line.split()        cmd = parts[0]        if cmd == "flip":            glasses[int(parts[1]) - 1].flip()        elif cmd == "wait":            t = int(parts[1])            elapsed += t            done = 0            for g in glasses:                if g.run(t):                    done += 1            out.append(str(done))        elif cmd == "until":            # 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)            t = glasses[int(parts[1]) - 1].remaining()            elapsed += t            for g in glasses:                g.run(t)            out.append(str(elapsed))        else:  # flipdone            flipped = 0            for g in glasses:                if g.is_empty():  # 괄호를 빼면 메서드 자체라 늘 참이다                    g.flip()                    flipped += 1            out.append(str(flipped))    for g in glasses:        out.append(f"{g.top} {g.bottom}")    print("\n".join(out))  main()
JavaScriptmain.js · 64 行
class Hourglass {  constructor(capacity) {    // 처음에는 모래가 전부 아래 칸에 있다    this.top = 0;    this.bottom = capacity;  }   flip() {    [this.top, this.bottom] = [this.bottom, this.top];  }   // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다  run(seconds) {    const moved = Math.min(seconds, this.top);    this.top -= moved;    this.bottom += moved;    return moved > 0 && this.top === 0;  }   isEmpty() {    return this.top === 0;  }   remaining() {    return this.top;  }} const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const glasses = lines[1].trim().split(" ").slice(0, n).map((c) => new Hourglass(Number(c)));let elapsed = 0;const out = [];for (let k = 2; k < 2 + q; k++) {  const [cmd, arg] = lines[k].trim().split(" ");  if (cmd === "flip") {    glasses[Number(arg) - 1].flip();  } else if (cmd === "wait") {    const t = Number(arg);    elapsed += t;    let done = 0;    for (const g of glasses) {      if (g.run(t)) done++;    }    out.push(done);  } else if (cmd === "until") {    // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)    const t = glasses[Number(arg) - 1].remaining();    elapsed += t;    for (const g of glasses) g.run(t);    out.push(elapsed);  } else {    let flipped = 0;    for (const g of glasses) {      if (g.isEmpty()) { // 괄호를 빼면 함수 자체라 늘 참이다        g.flip();        flipped++;      }    }    out.push(flipped);  }}for (const g of glasses) out.push(`${g.top} ${g.bottom}`);console.log(out.join("\n"));
TypeScriptmain.ts · 68 行
import { readFileSync } from "node:fs"; class Hourglass {  private top = 0; // 처음에는 모래가 전부 아래 칸에 있다   constructor(private bottom: number) {}   flip(): void {    [this.top, this.bottom] = [this.bottom, this.top];  }   // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다  run(seconds: number): boolean {    const moved = Math.min(seconds, this.top);    this.top -= moved;    this.bottom += moved;    return moved > 0 && this.top === 0;  }   isEmpty(): boolean {    return this.top === 0;  }   remaining(): number {    return this.top;  }   describe(): string {    return `${this.top} ${this.bottom}`;  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const glasses: Hourglass[] = lines[1].trim().split(" ").slice(0, n).map((c) => new Hourglass(Number(c)));let elapsed = 0;const out: string[] = [];for (let k = 2; k < 2 + q; k++) {  const [cmd, arg] = lines[k].trim().split(" ");  if (cmd === "flip") {    glasses[Number(arg) - 1].flip();  } else if (cmd === "wait") {    const t = Number(arg);    elapsed += t;    let done = 0;    for (const g of glasses) {      if (g.run(t)) done++;    }    out.push(String(done));  } else if (cmd === "until") {    // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)    const t = glasses[Number(arg) - 1].remaining();    elapsed += t;    for (const g of glasses) g.run(t);    out.push(String(elapsed));  } else {    let flipped = 0;    for (const g of glasses) {      if (g.isEmpty()) { // 괄호를 빼면 TypeScript가 컴파일 오류로 알려 준다        g.flip();        flipped++;      }    }    out.push(String(flipped));  }}for (const g of glasses) out.push(g.describe());console.log(out.join("\n"));
JavaMain.java · 83 行
import java.io.*;import java.util.*; public class Main {    static class Hourglass {        private int top = 0; // 처음에는 모래가 전부 아래 칸에 있다        private int bottom;         Hourglass(int capacity) {            this.bottom = capacity;        }         void flip() {            int t = top;            top = bottom;            bottom = t;        }         // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다        boolean run(int seconds) {            int moved = Math.min(seconds, top);            top -= moved;            bottom += moved;            return moved > 0 && top == 0;        }         boolean isEmpty() {            return top == 0;        }         int remaining() {            return top;        }         String describe() {            return top + " " + bottom;        }    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(br.readLine());        int n = Integer.parseInt(st.nextToken());        int q = Integer.parseInt(st.nextToken());        st = new StringTokenizer(br.readLine());        Hourglass[] glasses = new Hourglass[n];        for (int i = 0; i < n; i++) glasses[i] = new Hourglass(Integer.parseInt(st.nextToken()));        int elapsed = 0;        StringBuilder sb = new StringBuilder();        for (int k = 0; k < q; k++) {            st = new StringTokenizer(br.readLine());            String cmd = st.nextToken();            if (cmd.equals("flip")) {                glasses[Integer.parseInt(st.nextToken()) - 1].flip();            } else if (cmd.equals("wait")) {                int t = Integer.parseInt(st.nextToken());                elapsed += t;                int done = 0;                for (Hourglass g : glasses) {                    if (g.run(t)) done++;                }                sb.append(done).append('\n');            } else if (cmd.equals("until")) {                // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)                int t = glasses[Integer.parseInt(st.nextToken()) - 1].remaining();                elapsed += t;                for (Hourglass g : glasses) g.run(t);                sb.append(elapsed).append('\n');            } else {                int flipped = 0;                for (Hourglass g : glasses) {                    if (g.isEmpty()) {                        g.flip();                        flipped++;                    }                }                sb.append(flipped).append('\n');            }        }        for (Hourglass g : glasses) sb.append(g.describe()).append('\n');        System.out.print(sb);    }}
C#Main.cs · 99 行
using System;using System.Linq;using System.Text; class Hourglass{    private int top = 0; // 처음에는 모래가 전부 아래 칸에 있다    private int bottom;     public Hourglass(int capacity)    {        bottom = capacity;    }     public void Flip()    {        (top, bottom) = (bottom, top);    }     // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다    public bool Run(int seconds)    {        int moved = Math.Min(seconds, top);        top -= moved;        bottom += moved;        return moved > 0 && top == 0;    }     public bool IsEmpty()    {        return top == 0;    }     public int Remaining()    {        return top;    }     public string Describe()    {        return $"{top} {bottom}";    }} class Program{    static void Main()    {        int[] head = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();        int q = head[1];        Hourglass[] glasses = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries)            .Select(c => new Hourglass(int.Parse(c))).ToArray();        int elapsed = 0;        var sb = new StringBuilder();        for (int k = 0; k < q; k++)        {            string[] parts = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries);            string cmd = parts[0];            if (cmd == "flip")            {                glasses[int.Parse(parts[1]) - 1].Flip();            }            else if (cmd == "wait")            {                int t = int.Parse(parts[1]);                elapsed += t;                int done = 0;                foreach (Hourglass g in glasses)                {                    if (g.Run(t)) done++;                }                sb.Append(done).Append('\n');            }            else if (cmd == "until")            {                // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)                int t = glasses[int.Parse(parts[1]) - 1].Remaining();                elapsed += t;                foreach (Hourglass g in glasses) g.Run(t);                sb.Append(elapsed).Append('\n');            }            else            {                int flipped = 0;                foreach (Hourglass g in glasses)                {                    if (g.IsEmpty())                    {                        g.Flip();                        flipped++;                    }                }                sb.Append(flipped).Append('\n');            }        }        foreach (Hourglass g in glasses) sb.Append(g.Describe()).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 78 行
#include <algorithm>#include <iostream>#include <string>#include <utility>#include <vector> class Hourglass {public:    explicit Hourglass(int capacity) : bottom_(capacity) {}     void flip() { std::swap(top_, bottom_); }     // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다    bool run(int seconds) {        int moved = std::min(seconds, top_);        top_ -= moved;        bottom_ += moved;        return moved > 0 && top_ == 0;    }     // 값을 바꾸지 않는 메서드에는 const를 붙인다    bool isEmpty() const { return top_ == 0; }    int remaining() const { return top_; }    int bottom() const { return bottom_; } private:    int top_ = 0; // 처음에는 모래가 전부 아래 칸에 있다    int bottom_;}; int main() {    std::ios::sync_with_stdio(false);    int n, q;    std::cin >> n >> q;    std::vector<Hourglass> glasses;    for (int i = 0; i < n; i++) {        int c;        std::cin >> c;        glasses.emplace_back(c);    }    int elapsed = 0;    for (int k = 0; k < q; k++) {        std::string cmd;        std::cin >> cmd;        if (cmd == "flip") {            int i;            std::cin >> i;            glasses[i - 1].flip();        } else if (cmd == "wait") {            int t;            std::cin >> t;            elapsed += t;            int done = 0;            for (Hourglass& g : glasses) { // &가 없으면 복사본에 시간이 흐른다                if (g.run(t)) done++;            }            std::cout << done << '\n';        } else if (cmd == "until") {            int i;            std::cin >> i;            // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)            int t = glasses[i - 1].remaining();            elapsed += t;            for (Hourglass& g : glasses) g.run(t);            std::cout << elapsed << '\n';        } else {            int flipped = 0;            for (Hourglass& g : glasses) {                if (g.isEmpty()) {                    g.flip();                    flipped++;                }            }            std::cout << flipped << '\n';        }    }    for (const Hourglass& g : glasses) std::cout << g.remaining() << ' ' << g.bottom() << '\n';}