Skip to content

Commit 23fd48b

Browse files
committed
atcoder B09 - Papers 2D差分法 + 累積和
1 parent df2307d commit 23fd48b

6 files changed

Lines changed: 630 additions & 0 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// 以下は、**Go (Golang)** による高速・省メモリな解法です。
2+
// **2D差分法 + 累積和**で、1回以上塗られたセルの数(=面積)を求めます。
3+
4+
// ## ✅ 問題の制約と目的
5+
6+
// * 入力:`N ≤ 100000`、各長方形は座標 `(A,B)-(C,D)` の軸に平行な矩形
7+
// * 範囲:`0 ≤ A_i < C_i ≤ 1500`, `0 ≤ B_i < D_i ≤ 1500`
8+
// * **出力**:紙が1回以上置かれた2Dグリッド上のマス数(面積)
9+
10+
// ## ✅ Go実装(型明示・メモリ効率重視)
11+
12+
package main
13+
14+
import (
15+
"bufio"
16+
"fmt"
17+
"os"
18+
"strconv"
19+
"strings"
20+
)
21+
22+
const gridSize int = 1502 // 最大1501に対応するため+1
23+
24+
func main() {
25+
reader := bufio.NewReaderSize(os.Stdin, 1<<20)
26+
scanner := bufio.NewScanner(reader)
27+
scanner.Split(bufio.ScanLines)
28+
29+
// Read N
30+
scanner.Scan()
31+
N, _ := strconv.Atoi(scanner.Text())
32+
33+
// 2D差分グリッド定義(int16で十分)
34+
var grid [gridSize][gridSize]int16
35+
36+
// 入力処理と差分追加(O(N))
37+
for i := 0; i < N; i++ {
38+
scanner.Scan()
39+
line := scanner.Text()
40+
fields := strings.Fields(line)
41+
a, _ := strconv.Atoi(fields[0])
42+
b, _ := strconv.Atoi(fields[1])
43+
c, _ := strconv.Atoi(fields[2])
44+
d, _ := strconv.Atoi(fields[3])
45+
46+
grid[a][b]++
47+
grid[c][b]--
48+
grid[a][d]--
49+
grid[c][d]++
50+
}
51+
52+
// 横方向累積
53+
for x := 0; x < gridSize; x++ {
54+
for y := 1; y < gridSize; y++ {
55+
grid[x][y] += grid[x][y-1]
56+
}
57+
}
58+
59+
// 縦方向累積
60+
for y := 0; y < gridSize; y++ {
61+
for x := 1; x < gridSize; x++ {
62+
grid[x][y] += grid[x-1][y]
63+
}
64+
}
65+
66+
// 面積(1以上のマスをカウント)
67+
area := 0
68+
for x := 0; x < 1501; x++ {
69+
for y := 0; y < 1501; y++ {
70+
if grid[x][y] > 0 {
71+
area++
72+
}
73+
}
74+
}
75+
76+
fmt.Println(area)
77+
}
78+
79+
// ## ✅ 型とメモリ・時間の見積もり
80+
81+
// | 要素 | 内容 |
82+
// | -------- | ---------------------------------------------------- |
83+
// | 配列型 | `var grid [1502][1502]int16`(2バイト×1502×1502 ≈ 4.5MB) |
84+
// | 時間計算量 | `O(N + H×W)`(最大約 2.3×10⁶) |
85+
// | 実行時間の見込み | 約 100〜200ms(Goの高速処理能力) |
86+
// | メモリ消費 | 約 4.5MB(1024MB 制限の << 1%) |
87+
88+
// ---
89+
90+
// ## ✅ 入力例と出力
91+
92+
// **入力**
93+
94+
// ```
95+
// 2
96+
// 1 1 3 3
97+
// 2 2 4 4
98+
// ```
99+
100+
// **出力**
101+
102+
// ```
103+
// 7
104+
// ```
105+
106+
// ## ✅ 解説補足
107+
108+
// * `grid[a][b]++` など、**4点だけ更新**すれば後で累積和を取ることで内部が塗られる。
109+
// * Goでは固定長配列 `[1502][1502]int16` を使うことでGC負荷を最小化しつつ、定数アクセス可能。
110+
// * `bufio.Scanner` を使うことで10万件でも高速に読み取り。
111+
112+
// ### 📌 注意点
113+
114+
// * `gridSize = 1502` にしている理由は、最大座標 `1501` に対して `grid[c][d]++` でオーバーしないようにするためです。
115+
// * 出力は `fmt.Println()` 一発。バッファ不要です。
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// この問題は **2D平面上の重なりを考慮した長方形の面積の合計** を求めるものです。
2+
// 制約より、`x` および `y` 座標は `0 ≤ A_i, B_i < C_i, D_i ≤ 1500` の範囲に収まるため、\*\*1501×1501の2次元グリッドを用いた累積処理(2D差分)\*\*で高速に処理できます。
3+
4+
// ### ✅ 解法方針(2D差分 + 累積和):
5+
6+
// 1. 長方形 `(A,B)-(C,D)` をグリッド上に差分配列として記録。
7+
// 2. 最後に累積和を取って、1以上の値が存在するセルをカウント。
8+
// 3. これにより、重なりを重複カウントせず、正確に面積を求められる。
9+
10+
// ### ✅ JavaScript(Node.js + `fs`)実装:
11+
const fs = require("fs");
12+
13+
const input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
14+
const N = parseInt(input[0]);
15+
const gridSize = 1502; // 0-indexedで最大1501まで扱えるように
16+
17+
// 2D差分用グリッド
18+
const grid = Array.from({ length: gridSize }, () => new Int16Array(gridSize));
19+
20+
// 差分を記録
21+
for (let i = 1; i <= N; i++) {
22+
const [a, b, c, d] = input[i].split(" ").map(Number);
23+
grid[a][b] += 1;
24+
grid[c][b] -= 1;
25+
grid[a][d] -= 1;
26+
grid[c][d] += 1;
27+
}
28+
29+
// 横方向に累積和
30+
for (let x = 0; x < gridSize; x++) {
31+
for (let y = 1; y < gridSize; y++) {
32+
grid[x][y] += grid[x][y - 1];
33+
}
34+
}
35+
36+
// 縦方向に累積和
37+
for (let y = 0; y < gridSize; y++) {
38+
for (let x = 1; x < gridSize; x++) {
39+
grid[x][y] += grid[x - 1][y];
40+
}
41+
}
42+
43+
// 面積(1以上のセル数)をカウント
44+
let area = 0;
45+
for (let x = 0; x < 1501; x++) {
46+
for (let y = 0; y < 1501; y++) {
47+
if (grid[x][y] > 0) area++;
48+
}
49+
}
50+
51+
console.log(area);
52+
53+
// ### ✅ 実行時間・メモリ使用量の目安
54+
55+
// * **実行時間**: `O(N + H×W)`(≒ 1e5 + 1500×1500 = 約2.3e6) → 非常に高速
56+
// * **メモリ**: 約 `1500 x 1500 x 2byte` = 約 4.5MB → メモリ制限 1024MB に対して余裕あり
57+
58+
// ---
59+
60+
// ### ✅ 入力例テスト
61+
62+
// **入力**
63+
64+
// ```
65+
// 2
66+
// 1 1 3 3
67+
// 2 2 4 4
68+
// ```
69+
70+
// **出力**
71+
72+
// ```
73+
// 7
74+
// ```
75+
76+
// ### ✅ ポイント
77+
78+
// * `Int16Array` 使用でメモリ効率向上
79+
// * 差分更新 → 累積和の2段階処理で高速に重なり面積を正確カウント
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<!-- 以下に、\*\*PHP(バージョン8.2以降)\*\*での解法を示します。
2+
制約:`1 ≤ N ≤ 100000`、座標範囲:`0 ≤ A_i < C_i ≤ 1500`、`0 ≤ B_i < D_i ≤ 1500` --- ## ✅ 解法方針(2D差分法+累積和) * 各長方形は `(A, B)-(C, D)` の範囲を塗る。 * 差分配列を `4点更新` し、2次元累積和で実際の塗り回数を算出。 * 最後に「1枚以上の紙で覆われた」セルの数をカウント。 --- ## ✅ PHP 実装(型コメント付き) ```php -->
3+
<?php
4+
5+
function main(): void {
6+
// 入力を取得
7+
$input = explode("\n", trim(file_get_contents("php://stdin")));
8+
$N = (int)$input[0];
9+
$gridSize = 1502; // 境界処理のため+1
10+
11+
// 2次元配列(grid)を初期化:int型
12+
/** @var array<int, array<int, int>> */
13+
$grid = array_fill(0, $gridSize, array_fill(0, $gridSize, 0));
14+
15+
// 差分配列に対して4点更新
16+
for ($i = 1; $i <= $N; $i++) {
17+
[$a, $b, $c, $d] = array_map('intval', explode(' ', $input[$i]));
18+
19+
$grid[$a][$b] += 1;
20+
$grid[$c][$b] -= 1;
21+
$grid[$a][$d] -= 1;
22+
$grid[$c][$d] += 1;
23+
}
24+
25+
// 横方向に累積
26+
for ($x = 0; $x < $gridSize; $x++) {
27+
for ($y = 1; $y < $gridSize; $y++) {
28+
$grid[$x][$y] += $grid[$x][$y - 1];
29+
}
30+
}
31+
32+
// 縦方向に累積
33+
for ($y = 0; $y < $gridSize; $y++) {
34+
for ($x = 1; $x < $gridSize; $x++) {
35+
$grid[$x][$y] += $grid[$x - 1][$y];
36+
}
37+
}
38+
39+
// 1以上のマス(=覆われている面積)をカウント
40+
$area = 0;
41+
for ($x = 0; $x < 1501; $x++) {
42+
for ($y = 0; $y < 1501; $y++) {
43+
if ($grid[$x][$y] > 0) {
44+
$area++;
45+
}
46+
}
47+
}
48+
49+
echo $area . "\n";
50+
}
51+
52+
main();
53+
54+
// ## ✅ 処理時間・メモリ使用量の目安
55+
56+
// | 指標 | 内容 |
57+
// | ------ | ----------------------------- |
58+
// | 時間計算量 | `O(N + H × W)`(H=W=1501) |
59+
// | 実行時間 | 約 200~400ms(PHP 8.2, CLI環境) |
60+
// | メモリ使用量 | 約 9MB(1502×1502×4バイト = 約9MiB) |
61+
// | メモリ制限 | ✅ 1024MiB制限以内 |
62+
63+
// ---
64+
65+
// ## ✅ 入力例と出力
66+
67+
// **入力**
68+
69+
// ```
70+
// 2
71+
// 1 1 3 3
72+
// 2 2 4 4
73+
// ```
74+
75+
// **出力**
76+
77+
// ```
78+
// 7
79+
// ```
80+
81+
// ---
82+
83+
// ## ✅ なぜ差分法か?
84+
85+
// 長方形が `10^5` 枚あっても、**各更新は O(1)** なので全体で `O(N)`
86+
// → 全体にループしながら塗るより数百倍効率的です。
87+
88+
// ---
89+
90+
// ## 📌 注意点
91+
92+
// * PHPの配列はオブジェクトベースであるため、**`array_fill` を使って初期化**し、未定義アクセスによる `E_WARNING` を防止。
93+
// * 型ヒントはコメントで明示しました(PHPStan/Larastan等で補完可)。
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# 以下に、Python を用いた高速な解法を示します。
2+
# **型ヒント付き・メモリ効率重視・高速処理**を意識しています。
3+
4+
# ## ✅ 解法:2D差分法 + 累積和(Python版)
5+
6+
# ### 🔍 アプローチの要点
7+
8+
# * **制約**:
9+
10+
# * 最大 `1500 × 1500` の範囲で、`N ≤ 10⁵`。
11+
# * 2次元配列を使って差分更新 → 累積和で正確な重なり面積を高速取得。
12+
# * **Pythonでの工夫**:
13+
14+
# * `array` モジュールを使用してメモリ削減(`int16` 型使用)。
15+
16+
# ---
17+
18+
# ### ✅ コード(型付き)
19+
20+
# ```python
21+
from typing import List
22+
from array import array
23+
import sys
24+
25+
def main() -> None:
26+
input = sys.stdin.read
27+
data = input().split()
28+
29+
N: int = int(data[0])
30+
grid_size: int = 1502 # 境界対策(最大1501含むため)
31+
32+
# メモリ効率のため array('h') = signed short(int16)
33+
grid: List[array] = [array('h', [0] * grid_size) for _ in range(grid_size)] # type: ignore
34+
35+
idx: int = 1
36+
for _ in range(N):
37+
a: int = int(data[idx])
38+
b: int = int(data[idx+1])
39+
c: int = int(data[idx+2])
40+
d: int = int(data[idx+3])
41+
idx += 4
42+
43+
grid[a][b] += 1
44+
grid[c][b] -= 1
45+
grid[a][d] -= 1
46+
grid[c][d] += 1
47+
48+
# 横方向累積和
49+
for x in range(grid_size):
50+
for y in range(1, grid_size):
51+
grid[x][y] += grid[x][y - 1]
52+
53+
# 縦方向累積和
54+
for y in range(grid_size):
55+
for x in range(1, grid_size):
56+
grid[x][y] += grid[x - 1][y]
57+
58+
# 面積(1枚以上の紙で覆われたセル)をカウント
59+
area: int = 0
60+
for x in range(1501):
61+
for y in range(1501):
62+
if grid[x][y] > 0:
63+
area += 1
64+
65+
print(area)
66+
67+
if __name__ == "__main__":
68+
main()
69+
# ```
70+
71+
# ---
72+
73+
# ## 📊 計算量とメモリ
74+
75+
# | 指標 | 値 |
76+
# | ------- | --------------------------------- |
77+
# | 時間計算量 | `O(N + H × W)`(H=W=1501) |
78+
# | 実行時間目安 | 約 200ms ~ 400ms(CPython環境) |
79+
# | メモリ使用量 | 約 4.5MB(1502² × 2バイト) |
80+
# | 使用データ構造 | `array('h')`(signed short, int16) |
81+
82+
# ---
83+
84+
# ## ✅ 入力例と出力
85+
86+
# **入力**
87+
88+
# ```
89+
# 2
90+
# 1 1 3 3
91+
# 2 2 4 4
92+
# ```
93+
94+
# **出力**
95+
96+
# ```
97+
# 7
98+
# ```
99+

0 commit comments

Comments
 (0)