{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 6개 언어 풀이

이 문제는 클래스와 메서드가 있는 Python·JavaScript·TypeScript·Java·C#·C++만 받아요(C에는 메서드가 없고, 크레이콘은 아직 클래스를 만들 수 없어요).
메서드를 이어 부르려면 메서드가 객체 자신을 돌려주면 돼요: 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';        }    }}