{ }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';        }    }}