一个小计算器有 26 个格子,分别以英文小写字母 a 到 z 命名。 所有格子一开始都是 0。 第一行给出命令数 q,接下来 q 行每行一条命令。 命令中的单词用一个空格分隔。 set x v 把整数 v 放进格子 x。 swap x y 交换格子 x 和格子 y 的值。 x 和 y 可能相同,这时什么也不变。 rot x y z 轮换三个不同格子的值:x 得到 y 原来的值,y 得到 z 原来的值,z 得到 x 原来的值。 let x1 x2 … xk = e1 e2 … ek 把项 ei 的值放进格子 xi(i = 1, …, k)。 左边的格子名互不相同。 这时先用执行这条命令之前的值把右边 k 个项全部算出来,然后再一次性放进 k 个格子。 项是一个格子名、一个非负整数,或者 u+w、u-w 的形式。 u 和 w 各是一个格子名或非负整数,中间没有空格。 show x1 x2 … xk 按这个顺序把格子 x1, x2, …, xk 的值用一个空格隔开输出在一行。 同一个格子可能出现多次。 依次处理命令,每个 show 输出一行。
输入
1 ≤ q ≤ 2,000。 set 的 v 在 -1,000,000 到 1,000,000 之间。 项中的整数在 0 到 1,000,000 之间。 let 和 show 的格子数 k 在 1 到 26 之间。 show 有 1 到 200 个。 所有格子的值始终在 -1,000,000,000 到 1,000,000,000 之间
输出
与 show 个数相同的行数
样例 1 · stdin
7
set a 1
let a b = a+b a
let a b = a+b a
let a b = a+b a
show a b
swap a b
show a b
stdout
3 2
2 3
样例 2 · stdin
9
set x 5
set y -7
set z 9
swap x x
show x
rot x y z
show x y z
let x y z = z-1 x+y 100
show x y z
stdout
5
-7 9 5
4 2 100
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
交换两个值的方法因语言而异。 有一次性赋值的写法时,比如 Python a, b = b, a、JS・TS [a, b] = [b, a]、C# (a, b) = (b, a),会先算完右边再赋值,所以 rot 也只要一行。 C++ 没有这种写法,但有 std::swap(a, b) 函数;Java、C、Craykon 则先把一个值寄存在临时变量 tmp 里。 let 的格子数 k 每次都不同,所以无论哪种语言,都要先把右边的值收集到数组(Python、Craykon 用列表)里再赋值。 陷阱 ① 每算出一个 let 的值就马上赋值:在 let a b = a+b a 中先改了 a,b 就会得到新的 a。 ② 起个新名字不等于复制:Python old = reg、JS const old = reg 指向同一个字典・数组,并不是快照。 要用 dict(reg)、[...reg] 复制。 ③ 不用临时变量的交换技巧:XOR(a ^= b; b ^= a; a ^= b)和加减法(a = a + b; b = a - b; a = a - b)在两个名字指向同一个格子时(swap x x)会把值变成 0。 加减法遇到大数还可能溢出。 ④ 不用临时变量把 rot 写成 x = y; y = z; z = x,z 会得到已经改过的 x(原来的 y)。 方向也容易搞反:x 是接收 y 的值的那一个。 ⑤ 项里的 - 总是减法。 项中的整数没有符号,所以在第一个字符之后找 + 或 - 就行。 Craykon 中,函数里赋值的名字如果在程序最外层也用到(且不是参数),就会改动外面的那个变量,所以函数里的循环变量要取与外面不同的名字。
Pythonmain.py · 42 行
import syslines = sys.stdin.read().split("\n")q = int(lines[0])reg = {chr(c): 0 for c in range(ord("a"), ord("z") + 1)}def atom(t): return reg[t] if t.isalpha() else int(t)def term(t): # p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없어서 -는 늘 빼기다 for op in "+-": i = t.find(op) if i > 0: a, b = atom(t[:i]), atom(t[i + 1:]) return a + b if op == "+" else a - b return atom(t)out = []for line in lines[1:1 + q]: p = line.split() cmd = p[0] if cmd == "set": reg[p[1]] = int(p[2]) elif cmd == "swap": x, y = p[1], p[2] reg[x], reg[y] = reg[y], reg[x] # x와 y가 같아도 그대로 elif cmd == "rot": x, y, z = p[1], p[2], p[3] reg[x], reg[y], reg[z] = reg[y], reg[z], reg[x] elif cmd == "let": eq = p.index("=") targets = p[1:eq] values = [term(t) for t in p[eq + 1:]] # 오른쪽을 모두 먼저 계산한다 for x, v in zip(targets, values): reg[x] = v else: # show out.append(" ".join(str(reg[x]) for x in p[1:]))print("\n".join(out))
JavaScriptmain.js · 40 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const reg = new Array(26).fill(0);const at = (x) => x.charCodeAt(0) - 97; // 'a' → 0const atom = (t) => (/^[a-z]$/.test(t) ? reg[at(t)] : Number(t));function term(t) { // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다 const i = t.search(/[+-]/); if (i <= 0) return atom(t); const a = atom(t.slice(0, i)); const b = atom(t.slice(i + 1)); return t[i] === "+" ? a + b : a - b;}const out = [];for (const line of lines.slice(1, 1 + q)) { const p = line.trim().split(" "); const cmd = p[0]; if (cmd === "set") { reg[at(p[1])] = Number(p[2]); } else if (cmd === "swap") { const x = at(p[1]); const y = at(p[2]); [reg[x], reg[y]] = [reg[y], reg[x]]; // 구조 분해로 한꺼번에 바꾼다 } else if (cmd === "rot") { const [x, y, z] = p.slice(1).map(at); [reg[x], reg[y], reg[z]] = [reg[y], reg[z], reg[x]]; } else if (cmd === "let") { const eq = p.indexOf("="); const values = p.slice(eq + 1).map(term); // 오른쪽을 모두 먼저 계산한다 p.slice(1, eq).forEach((x, i) => { reg[at(x)] = values[i]; }); } else { out.push(p.slice(1).map((x) => reg[at(x)]).join(" ")); }}console.log(out.join("\n"));
TypeScriptmain.ts · 42 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const reg: number[] = new Array<number>(26).fill(0);const at = (x: string): number => x.charCodeAt(0) - 97; // 'a' → 0const atom = (t: string): number => (/^[a-z]$/.test(t) ? reg[at(t)] : Number(t));function term(t: string): number { // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다 const i = t.search(/[+-]/); if (i <= 0) return atom(t); const a = atom(t.slice(0, i)); const b = atom(t.slice(i + 1)); return t[i] === "+" ? a + b : a - b;}const out: string[] = [];for (const line of lines.slice(1, 1 + q)) { const p = line.trim().split(" "); const cmd = p[0]; if (cmd === "set") { reg[at(p[1])] = Number(p[2]); } else if (cmd === "swap") { const x = at(p[1]); const y = at(p[2]); [reg[x], reg[y]] = [reg[y], reg[x]]; // 구조 분해로 한꺼번에 바꾼다 } else if (cmd === "rot") { const [x, y, z] = p.slice(1).map(at); [reg[x], reg[y], reg[z]] = [reg[y], reg[z], reg[x]]; } else if (cmd === "let") { const eq = p.indexOf("="); const values: number[] = p.slice(eq + 1).map(term); // 오른쪽을 모두 먼저 계산한다 p.slice(1, eq).forEach((x, i) => { reg[at(x)] = values[i]; }); } else { out.push(p.slice(1).map((x) => reg[at(x)]).join(" ")); }}console.log(out.join("\n"));
JavaMain.java · 68 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.StringJoiner;public class Main { static int[] reg = new int[26]; static int at(String x) { return x.charAt(0) - 'a'; } static int atom(String t) { return Character.isLetter(t.charAt(0)) ? reg[at(t)] : Integer.parseInt(t); } static int term(String t) { // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다 for (int i = 1; i < t.length(); i++) { char c = t.charAt(i); if (c == '+' || c == '-') { int a = atom(t.substring(0, i)); int b = atom(t.substring(i + 1)); return c == '+' ? a + b : a - b; } } return atom(t); } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int q = Integer.parseInt(in.readLine().trim()); StringBuilder out = new StringBuilder(); for (int t = 0; t < q; t++) { String[] p = in.readLine().trim().split(" "); switch (p[0]) { case "set" -> reg[at(p[1])] = Integer.parseInt(p[2]); case "swap" -> { int x = at(p[1]), y = at(p[2]); int tmp = reg[x]; // Java에는 a, b = b, a가 없어서 임시 변수를 쓴다 reg[x] = reg[y]; reg[y] = tmp; } case "rot" -> { int x = at(p[1]), y = at(p[2]), z = at(p[3]); int tmp = reg[x]; reg[x] = reg[y]; reg[y] = reg[z]; reg[z] = tmp; } case "let" -> { int eq = 1; while (!p[eq].equals("=")) eq++; int k = eq - 1; int[] values = new int[k]; // 오른쪽을 모두 먼저 계산해 둔다 for (int i = 0; i < k; i++) values[i] = term(p[eq + 1 + i]); for (int i = 0; i < k; i++) reg[at(p[1 + i])] = values[i]; } default -> { StringJoiner sj = new StringJoiner(" "); for (int i = 1; i < p.length; i++) sj.add(String.valueOf(reg[at(p[i])])); out.append(sj).append('\n'); } } } System.out.print(out); }}
C#Main.cs · 62 行
using System;using System.Linq;using System.Text;class Program{ static int[] reg = new int[26]; static int At(string x) => x[0] - 'a'; static int Atom(string t) => char.IsLetter(t[0]) ? reg[At(t)] : int.Parse(t); static int Term(string t) { // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다 int i = t.IndexOfAny(new[] { '+', '-' }, 1); if (i < 0) return Atom(t); int a = Atom(t.Substring(0, i)); int b = Atom(t.Substring(i + 1)); return t[i] == '+' ? a + b : a - b; } static void Main() { int q = int.Parse(Console.ReadLine().Trim()); var sb = new StringBuilder(); for (int t = 0; t < q; t++) { string[] p = Console.ReadLine().Trim().Split(' '); switch (p[0]) { case "set": reg[At(p[1])] = int.Parse(p[2]); break; case "swap": { int x = At(p[1]), y = At(p[2]); (reg[x], reg[y]) = (reg[y], reg[x]); // 튜플로 한꺼번에 바꾼다 break; } case "rot": { int x = At(p[1]), y = At(p[2]), z = At(p[3]); (reg[x], reg[y], reg[z]) = (reg[y], reg[z], reg[x]); break; } case "let": { int eq = Array.IndexOf(p, "="); // 오른쪽을 모두 먼저 계산해 배열로 받아 둔다 int[] values = p.Skip(eq + 1).Select(Term).ToArray(); for (int i = 0; i < values.Length; i++) reg[At(p[1 + i])] = values[i]; break; } default: sb.Append(string.Join(" ", p.Skip(1).Select(x => reg[At(x)]))).Append('\n'); break; } } Console.Write(sb); }}
C++main.cpp · 58 行
#include <array>#include <iostream>#include <sstream>#include <string>#include <utility>#include <vector>std::array<int, 26> reg{};int atom(const std::string &t) { return std::isalpha(static_cast<unsigned char>(t[0])) ? reg[t[0] - 'a'] : std::stoi(t);}int term(const std::string &t) { // 항 안의 수에는 부호가 없어서 첫 글자 뒤의 + 나 - 는 늘 계산 기호다 std::size_t i = t.find_first_of("+-", 1); if (i == std::string::npos) return atom(t); int a = atom(t.substr(0, i)), b = atom(t.substr(i + 1)); return t[i] == '+' ? a + b : a - b;}int main() { std::string line; std::getline(std::cin, line); int q = std::stoi(line); std::string out; for (int c = 0; c < q; c++) { std::getline(std::cin, line); std::istringstream ss(line); std::vector<std::string> p; for (std::string w; ss >> w;) p.push_back(w); if (p[0] == "set") { reg[p[1][0] - 'a'] = std::stoi(p[2]); } else if (p[0] == "swap") { std::swap(reg[p[1][0] - 'a'], reg[p[2][0] - 'a']); } else if (p[0] == "rot") { int &x = reg[p[1][0] - 'a'], &y = reg[p[2][0] - 'a'], &z = reg[p[3][0] - 'a']; int tmp = x; x = y; y = z; z = tmp; } else if (p[0] == "let") { std::size_t eq = 1; while (p[eq] != "=") eq++; std::vector<int> values; // 오른쪽을 모두 먼저 계산한다 for (std::size_t i = eq + 1; i < p.size(); i++) values.push_back(term(p[i])); for (std::size_t i = 0; i < values.size(); i++) reg[p[1 + i][0] - 'a'] = values[i]; } else { for (std::size_t i = 1; i < p.size(); i++) { if (i > 1) out += ' '; out += std::to_string(reg[p[i][0] - 'a']); } out += '\n'; } } std::cout << out; return 0;}
Cmain.c · 60 行
#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h>static int reg[26];static int atom(const char *t, int len) { if (isalpha((unsigned char)t[0])) return reg[t[0] - 'a']; int v = 0; for (int i = 0; i < len; i++) v = v * 10 + (t[i] - '0'); return v;}/* p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없다 */static int term(const char *t) { int n = (int)strlen(t); for (int i = 1; i < n; i++) if (t[i] == '+' || t[i] == '-') { int a = atom(t, i), b = atom(t + i + 1, n - i - 1); return t[i] == '+' ? a + b : a - b; } return atom(t, n);}int main(void) { static char line[1024]; char *tok[64]; fgets(line, sizeof line, stdin); int q = atoi(line); for (int c = 0; c < q; c++) { fgets(line, sizeof line, stdin); int k = 0; for (char *s = strtok(line, " \r\n"); s; s = strtok(NULL, " \r\n")) tok[k++] = s; if (strcmp(tok[0], "set") == 0) { reg[tok[1][0] - 'a'] = atoi(tok[2]); } else if (strcmp(tok[0], "swap") == 0) { int x = tok[1][0] - 'a', y = tok[2][0] - 'a'; int tmp = reg[x]; /* 임시 칸에 하나를 맡겨 둔다 */ reg[x] = reg[y]; reg[y] = tmp; } else if (strcmp(tok[0], "rot") == 0) { int x = tok[1][0] - 'a', y = tok[2][0] - 'a', z = tok[3][0] - 'a'; int tmp = reg[x]; reg[x] = reg[y]; reg[y] = reg[z]; reg[z] = tmp; } else if (strcmp(tok[0], "let") == 0) { int eq = 1; while (strcmp(tok[eq], "=") != 0) eq++; int n = eq - 1, values[26]; for (int i = 0; i < n; i++) values[i] = term(tok[eq + 1 + i]); /* 먼저 모두 계산 */ for (int i = 0; i < n; i++) reg[tok[1 + i][0] - 'a'] = values[i]; } else { for (int i = 1; i < k; i++) printf(i > 1 ? " %d" : "%d", reg[tok[i][0] - 'a']); putchar('\n'); } } return 0;}
Craykonmain.cray · 77 行
q = 控制台.读行().转数字();reg = 字典();循环 (i = 0; i < 26; i++){ reg[字符.从编码(97 + i)] = 0;}하나(t){ 如果 (reg.包含(t)) { 返回 reg[t]; } 返回 t.转数字();}// p+q, p-q, 또는 칸 이름·수 하나. 항 안의 수에는 부호가 없다항(t){ // 함수 안에서 넣은 이름이 밖에도 있으면 밖의 변수가 바뀌니, 밖에서 안 쓰는 이름(w)을 쓴다 循环 (w = 1; w < t.长度; w++) { 如果 (t[w] == "+" || t[w] == "-") { 가 = 하나(t.截取(0, w)); 나머지 = 하나(t.截取(w + 1)); 如果 (t[w] == "+") { 返回 가 + 나머지; } 返回 가 - 나머지; } } 返回 하나(t);}循环 (c = 0; c < q; c++){ p = 控制台.读行().去空白().分割(" "); 选择 (p[0]) { 情况 "set": reg[p[1]] = p[2].转数字(); 跳出; 情况 "swap": 임시 = reg[p[1]]; // 크레이콘에는 한꺼번에 넣기가 없어서 임시 변수에 맡긴다 reg[p[1]] = reg[p[2]]; reg[p[2]] = 임시; 跳出; 情况 "rot": 임시 = reg[p[1]]; reg[p[1]] = reg[p[2]]; reg[p[2]] = reg[p[3]]; reg[p[3]] = 임시; 跳出; 情况 "let": eq = p.查找位置("="); 값들 = []; // 오른쪽을 모두 먼저 계산해 둔다 循环 (i = eq + 1; i < p.数量; i++) { 값들.添加(항(p[i])); } 循环 (i = 0; i < 값들.数量; i++) { reg[p[1 + i]] = 값들[i]; } 跳出; 默认: 보일것 = []; 循环 (i = 1; i < p.数量; i++) { 보일것.添加(reg[p[i]]); } 控制台.输出(보일것.连接(" ")); 跳出; }}