{ }codelper
方法类64 位整数P0276

用链式调用的方法修改房间平面图

平面图上的房间是边与横轴、纵轴平行的矩形,用左下顶点 (x1, y1) 和右上顶点 (x2, y2) 表示(x1 < x2,y1 < y2)。
把房间写成类 Room,并提供以下方法。
move(dx, dy) 把房间横向移动 dx、纵向移动 dy。
scale(k) 以原点为中心把房间放大 k 倍,也就是把顶点的所有坐标乘以 k。
turn(t) 以原点为中心把房间逆时针旋转 90 度,共 t 次。
旋转一次时点 (x, y) 变到 (−y, x)。
旋转之后房间仍用左下和右上顶点表示。
move、scale、turn 返回该房间对象本身,使它们可以像 room.move(dx, dy).scale(k).turn(t) 这样链式调用。
copy() 返回位置相同的新房间。
复制之后两个房间各自移动。
area() 返回面积,overlap(other) 返回两个房间重叠部分的面积。
只在边或顶点处接触时,重叠面积为 0。
第一行给出命令数 q,接下来 q 行每行一条命令。
“new 名字 x1 y1 x2 y2”新建该名字的房间。
名字已存在时换成新房间。
“copy 新名字 名字”用 copy() 复制名字的房间放到新名字下。
新名字已存在则替换,两个名字也可能相同。
“do 名字 dx dy k t”对该房间在一行中链式调用 move(dx, dy).scale(k).turn(t)。
“area 名字”输出面积,“overlap 名字1 名字2”输出重叠面积,“show 名字”在一行中输出“x1 y1 x2 y2”。

输入
1 ≤ q ≤ 2,000。
名字是 1 ~ 10 个英文小写字母或数字。
除 new、copy 以外的命令中的名字以及 copy 的原名字,都是已存在的房间。
−1,000,000 ≤ dx, dy ≤ 1,000,000,1 ≤ k ≤ 3,0 ≤ t ≤ 3。
所有房间的坐标在每条命令处理之后、以及一行中每调用一个方法之后,始终是 −1,000,000 以上 1,000,000 以下的整数
输出
每条 area、overlap、show 命令输出一行(面积只输出数字,不加逗号)
样例 1 · stdin
9
new a 0 0 4 2
copy b a
do b 1 1 2 1
show a
show b
area b
new c -3 -1 0 5
overlap b c
overlap a c
stdout
0 0 4 2
-6 2 -2 10
32
3
0
main.py
输入(stdin)

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

$ 就绪

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

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

这道题只接受有类和方法的 Python、JavaScript、TypeScript、Java、C#、C++(C 没有方法,Craykon 还不能定义类)。
要链式调用方法,让方法返回对象本身即可:Python return self,JS/Java/C# return this,TypeScript 把返回类型写成 this 并 return this,C++ 返回 Room& 并 return *this。
最大的区别在于“本身”是以引用还是以副本传出。
Python、JS、TS、Java、C# 的类对象总是引用,所以返回 this 后下一个方法改的还是同一个房间。
C++ 中按值返回 Room 会得到副本,rooms[a].move(…).scale(…) 从 scale 开始改的都是副本,改完就没了 — 能编译,也没有警告。
反过来,复制时引用是陷阱:在 Python、JS、TS、Java、C# 中 rooms[b] = rooms[a] 只是让两个名字指向同一个对象,移动 b 时 a 也会动。
所以需要新建对象的 copy()。
C++ 按值存放对象,rooms[b] = rooms[a] 本身就是复制(拷贝构造函数起到 copy() 的作用)。
C# 中如果把 Room 写成 struct,每次从字典取出都是副本,修改会丢失,所以要写成 class。
旋转:旋转一次后左下 (x1, y1) 变到 (−y1, x1),成了右下,所以新的左下是 (−y2, x1),新的右上是 (−y1, x2) — 只把两个顶点照原样旋转会得到 x1 > x2,面积成了负数。
重叠面积是 max(0, 宽) × max(0, 高) — 直接相乘时,沿对角线分开的两个房间会变成负 × 负 = 正。
面积可达 (2×10^6)² = 4×10^12,所以 Java、C#、C++ 要用 long、long long(两个 int 相乘会溢出),Python 的整数没有上限,JS/TS 的数在 2^53 以内是精确的,直接用就行。
例:b 从 (1, 1, 5, 3) → (2, 2, 10, 6) → 旋转后成为 (−6, 2, −2, 10),a 保持不动。

Pythonmain.py · 66 行
import sys  class Room:    """도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)"""     def __init__(self, x1, y1, x2, y2):        self.x1, self.y1, self.x2, self.y2 = x1, y1, x2, y2     # move·scale·turn은 자기 자신(self)을 돌려준다: room.move(...).scale(...).turn(...)처럼 이어 부를 수 있다    def move(self, dx, dy):        self.x1 += dx        self.x2 += dx        self.y1 += dy        self.y2 += dy        return self     def scale(self, k):        self.x1 *= k        self.y1 *= k        self.x2 *= k        self.y2 *= k        return self     def turn(self, times):        for _ in range(times):            # 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다            self.x1, self.y1, self.x2, self.y2 = -self.y2, self.x1, -self.y1, self.x2        return self     def copy(self):        return Room(self.x1, self.y1, self.x2, self.y2)  # 새 객체: 앞으로 따로 움직인다     def area(self):        return (self.x2 - self.x1) * (self.y2 - self.y1)     def overlap(self, other):        w = min(self.x2, other.x2) - max(self.x1, other.x1)        h = min(self.y2, other.y2) - max(self.y1, other.y1)        return max(0, w) * max(0, h)  # 한쪽이라도 0 이하면 겹치지 않는다     def __str__(self):        return f"{self.x1} {self.y1} {self.x2} {self.y2}"  lines = sys.stdin.read().split("\n")q = int(lines[0])rooms = {}out = []for i in range(1, q + 1):    p = lines[i].split()    cmd = p[0]    if cmd == "new":        rooms[p[1]] = Room(*map(int, p[2:6]))    elif cmd == "copy":        rooms[p[1]] = rooms[p[2]].copy()    elif cmd == "do":        dx, dy, k, t = map(int, p[2:6])        rooms[p[1]].move(dx, dy).scale(k).turn(t)    elif cmd == "area":        out.append(str(rooms[p[1]].area()))    elif cmd == "overlap":        out.append(str(rooms[p[1]].overlap(rooms[p[2]])))    else:  # show        out.append(str(rooms[p[1]]))print("\n".join(out))
JavaScriptmain.js · 70 行
const lines = require("fs").readFileSync(0, "utf8").split("\n"); // 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)class Room {  constructor(x1, y1, x2, y2) {    this.x1 = x1;    this.y1 = y1;    this.x2 = x2;    this.y2 = y2;  }   // move·scale·turn은 자기 자신(this)을 돌려준다: room.move(...).scale(...).turn(...)처럼 이어 부를 수 있다  move(dx, dy) {    this.x1 += dx;    this.x2 += dx;    this.y1 += dy;    this.y2 += dy;    return this;  }   scale(k) {    this.x1 *= k;    this.y1 *= k;    this.x2 *= k;    this.y2 *= k;    return this;  }   turn(times) {    for (let i = 0; i < times; i++) {      // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다      [this.x1, this.y1, this.x2, this.y2] = [-this.y2, this.x1, -this.y1, this.x2];    }    return this;  }   copy() {    return new Room(this.x1, this.y1, this.x2, this.y2); // 새 객체: 앞으로 따로 움직인다  }   area() {    return (this.x2 - this.x1) * (this.y2 - this.y1); // 4×10^12까지: number는 2^53까지 정확하다  }   overlap(other) {    const w = Math.min(this.x2, other.x2) - Math.max(this.x1, other.x1);    const h = Math.min(this.y2, other.y2) - Math.max(this.y1, other.y1);    return Math.max(0, w) * Math.max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다  }   toString() {    return `${this.x1} ${this.y1} ${this.x2} ${this.y2}`;  }} const q = Number(lines[0]);const rooms = new Map();const out = [];for (let i = 1; i <= q; i++) {  const p = lines[i].trim().split(" ");  const [cmd, a, b] = p;  const num = p.slice(2).map(Number);  if (cmd === "new") rooms.set(a, new Room(num[0], num[1], num[2], num[3]));  else if (cmd === "copy") rooms.set(a, rooms.get(b).copy());  else if (cmd === "do") rooms.get(a).move(num[0], num[1]).scale(num[2]).turn(num[3]);  else if (cmd === "area") out.push(rooms.get(a).area());  else if (cmd === "overlap") out.push(rooms.get(a).overlap(rooms.get(b)));  else out.push(String(rooms.get(a)));}console.log(out.join("\n"));
TypeScriptmain.ts · 73 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n"); // 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)class Room {  constructor(    private x1: number,    private y1: number,    private x2: number,    private y2: number,  ) {}   // move·scale·turn은 자기 자신을 돌려준다(돌려주는 타입 this): room.move(...).scale(...).turn(...)  move(dx: number, dy: number): this {    this.x1 += dx;    this.x2 += dx;    this.y1 += dy;    this.y2 += dy;    return this;  }   scale(k: number): this {    this.x1 *= k;    this.y1 *= k;    this.x2 *= k;    this.y2 *= k;    return this;  }   turn(times: number): this {    for (let i = 0; i < times; i++) {      // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다      [this.x1, this.y1, this.x2, this.y2] = [-this.y2, this.x1, -this.y1, this.x2];    }    return this;  }   copy(): Room {    return new Room(this.x1, this.y1, this.x2, this.y2); // 새 객체: 앞으로 따로 움직인다  }   area(): number {    return (this.x2 - this.x1) * (this.y2 - this.y1); // 4×10^12까지: number는 2^53까지 정확하다  }   overlap(other: Room): number {    const w = Math.min(this.x2, other.x2) - Math.max(this.x1, other.x1);    const h = Math.min(this.y2, other.y2) - Math.max(this.y1, other.y1);    return Math.max(0, w) * Math.max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다  }   toString(): string {    return `${this.x1} ${this.y1} ${this.x2} ${this.y2}`;  }} const q = Number(lines[0]);const rooms = new Map<string, Room>();const out: string[] = [];const get = (name: string): Room => rooms.get(name)!; // 입력에 나오는 이름은 모두 이미 있다for (let i = 1; i <= q; i++) {  const p = lines[i].trim().split(" ");  const [cmd, a, b] = p;  const num = p.slice(2).map(Number);  if (cmd === "new") rooms.set(a, new Room(num[0], num[1], num[2], num[3]));  else if (cmd === "copy") rooms.set(a, get(b).copy());  else if (cmd === "do") get(a).move(num[0], num[1]).scale(num[2]).turn(num[3]);  else if (cmd === "area") out.push(String(get(a).area()));  else if (cmd === "overlap") out.push(String(get(a).overlap(get(b))));  else out.push(get(a).toString());}console.log(out.join("\n"));
JavaMain.java · 66 行
import java.io.*;import java.util.*; public class Main {    // 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)    static class Room {        private long x1, y1, x2, y2;         Room(long x1, long y1, long x2, long y2) {            this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2;        }         // move·scale·turn은 자기 자신(this)을 돌려준다: room.move(...).scale(...).turn(...)처럼 이어 부를 수 있다        Room move(long dx, long dy) {            x1 += dx; x2 += dx;            y1 += dy; y2 += dy;            return this;        }         Room scale(long k) {            x1 *= k; y1 *= k; x2 *= k; y2 *= k;            return this;        }         Room turn(int times) {            for (int i = 0; i < times; i++) {                // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다                long nx1 = -y2, ny1 = x1, nx2 = -y1, ny2 = x2;                x1 = nx1; y1 = ny1; x2 = nx2; y2 = ny2;            }            return this;        }         Room copy() { return new Room(x1, y1, x2, y2); } // 새 객체: 앞으로 따로 움직인다         long area() { return (x2 - x1) * (y2 - y1); } // 4×10^12까지라 long         long overlap(Room o) {            long w = Math.min(x2, o.x2) - Math.max(x1, o.x1);            long h = Math.min(y2, o.y2) - Math.max(y1, o.y1);            return Math.max(0, w) * Math.max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다        }         @Override        public String toString() { return x1 + " " + y1 + " " + x2 + " " + y2; }    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(br.readLine().trim());        Map<String, Room> rooms = new HashMap<>();        StringBuilder sb = new StringBuilder();        for (int i = 0; i < q; i++) {            String[] p = br.readLine().trim().split(" ");            switch (p[0]) {                case "new" -> rooms.put(p[1], new Room(Long.parseLong(p[2]), Long.parseLong(p[3]), Long.parseLong(p[4]), Long.parseLong(p[5])));                case "copy" -> rooms.put(p[1], rooms.get(p[2]).copy());                case "do" -> rooms.get(p[1]).move(Long.parseLong(p[2]), Long.parseLong(p[3])).scale(Long.parseLong(p[4])).turn(Integer.parseInt(p[5]));                case "area" -> sb.append(rooms.get(p[1]).area()).append('\n');                case "overlap" -> sb.append(rooms.get(p[1]).overlap(rooms.get(p[2]))).append('\n');                default -> sb.append(rooms.get(p[1])).append('\n');            }        }        System.out.print(sb);    }}
C#Main.cs · 76 行
using System;using System.Collections.Generic;using System.Text; // 도면 위의 네모난 방: 왼쪽 아래 (X1, Y1), 오른쪽 위 (X2, Y2)// struct가 아니라 class: struct면 사전에서 꺼낼 때마다 복사본이 나와 바꾼 것이 사라진다class Room{    public long X1 { get; private set; }    public long Y1 { get; private set; }    public long X2 { get; private set; }    public long Y2 { get; private set; }     public Room(long x1, long y1, long x2, long y2) { X1 = x1; Y1 = y1; X2 = x2; Y2 = y2; }     // Move·Scale·Turn은 자기 자신(this)을 돌려준다: room.Move(...).Scale(...).Turn(...)처럼 이어 부를 수 있다    public Room Move(long dx, long dy)    {        X1 += dx; X2 += dx;        Y1 += dy; Y2 += dy;        return this;    }     public Room Scale(long k)    {        X1 *= k; Y1 *= k; X2 *= k; Y2 *= k;        return this;    }     public Room Turn(int times)    {        for (int i = 0; i < times; i++)        {            // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다            (X1, Y1, X2, Y2) = (-Y2, X1, -Y1, X2);        }        return this;    }     public Room Copy() => new Room(X1, Y1, X2, Y2); // 새 객체: 앞으로 따로 움직인다     public long Area() => (X2 - X1) * (Y2 - Y1); // 4×10^12까지라 long     public long Overlap(Room o)    {        long w = Math.Min(X2, o.X2) - Math.Max(X1, o.X1);        long h = Math.Min(Y2, o.Y2) - Math.Max(Y1, o.Y1);        return Math.Max(0, w) * Math.Max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다    }     public override string ToString() => $"{X1} {Y1} {X2} {Y2}";} class Program{    static void Main()    {        int q = int.Parse(Console.ReadLine()!.Trim());        var rooms = new Dictionary<string, Room>();        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            var p = Console.ReadLine()!.Trim().Split(' ');            switch (p[0])            {                case "new": rooms[p[1]] = new Room(long.Parse(p[2]), long.Parse(p[3]), long.Parse(p[4]), long.Parse(p[5])); break;                case "copy": rooms[p[1]] = rooms[p[2]].Copy(); break;                case "do": rooms[p[1]].Move(long.Parse(p[2]), long.Parse(p[3])).Scale(long.Parse(p[4])).Turn(int.Parse(p[5])); break;                case "area": sb.Append(rooms[p[1]].Area()).Append('\n'); break;                case "overlap": sb.Append(rooms[p[1]].Overlap(rooms[p[2]])).Append('\n'); break;                default: sb.Append(rooms[p[1]]).Append('\n'); break;            }        }        Console.Write(sb);    }}
C++main.cpp · 86 行
#include <algorithm>#include <iostream>#include <string>#include <unordered_map> // 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)class Room {public:    Room() = default;    Room(long long x1, long long y1, long long x2, long long y2) : x1_(x1), y1_(y1), x2_(x2), y2_(y2) {}     // move·scale·turn은 자기 자신의 참조(Room&)를 돌려준다: room.move(...).scale(...).turn(...)    // Room(값)으로 돌려주면 그다음 메서드는 복사본을 바꾸고, 원래 방에는 move만 남는다    Room &move(long long dx, long long dy) {        x1_ += dx, x2_ += dx;        y1_ += dy, y2_ += dy;        return *this;    }     Room &scale(long long k) {        x1_ *= k, y1_ *= k, x2_ *= k, y2_ *= k;        return *this;    }     Room &turn(int times) {        for (int i = 0; i < times; i++) {            // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다            long long nx1 = -y2_, ny1 = x1_, nx2 = -y1_, ny2 = x2_;            x1_ = nx1, y1_ = ny1, x2_ = nx2, y2_ = ny2;        }        return *this;    }     // 같은 자리의 새 방. 객체를 값으로 담는 C++에서는 복사 생성자가 하는 일과 같다    Room copy() const { return *this; }     long long area() const { return (x2_ - x1_) * (y2_ - y1_); } // 4×10^12까지라 long long     long long overlap(const Room &o) const {        long long w = std::min(x2_, o.x2_) - std::max(x1_, o.x1_);        long long h = std::min(y2_, o.y2_) - std::max(y1_, o.y1_);        return std::max(0LL, w) * std::max(0LL, h); // 한쪽이라도 0 이하면 겹치지 않는다    }     std::string str() const {        return std::to_string(x1_) + " " + std::to_string(y1_) + " " + std::to_string(x2_) + " " + std::to_string(y2_);    } private:    long long x1_ = 0, y1_ = 0, x2_ = 0, y2_ = 0;}; int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int q;    std::cin >> q;    // 사전에 객체를 값으로 담는다: rooms[b] = rooms[a]는 복사라서 copy() 메서드가 따로 없어도 둘이 따로 움직인다    std::unordered_map<std::string, Room> rooms;    for (int i = 0; i < q; i++) {        std::string cmd, a;        std::cin >> cmd >> a;        if (cmd == "new") {            long long x1, y1, x2, y2;            std::cin >> x1 >> y1 >> x2 >> y2;            rooms[a] = Room(x1, y1, x2, y2);        } else if (cmd == "copy") {            std::string b;            std::cin >> b;            rooms[a] = rooms[b].copy(); // 사전에 값으로 담으니 rooms[a] = rooms[b]만 써도 복사다        } else if (cmd == "do") {            long long dx, dy, k;            int t;            std::cin >> dx >> dy >> k >> t;            rooms[a].move(dx, dy).scale(k).turn(t);        } else if (cmd == "area") {            std::cout << rooms[a].area() << '\n';        } else if (cmd == "overlap") {            std::string b;            std::cin >> b;            std::cout << rooms[a].overlap(rooms[b]) << '\n';        } else {            std::cout << rooms[a].str() << '\n';        }    }}