Skip to content

Commit ad88a6d

Browse files
committed
leetcode 24. Swap Nodes in Pairs singly-linked list
1 parent 8f3e9e0 commit ad88a6d

7 files changed

Lines changed: 1064 additions & 0 deletions

File tree

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
以下に、**mod P 上での加算・減算・乗算**に関するクエリ処理を、**図とともに具体的かつ詳細に**解説します。Node.js 実装内容に沿って、**初期状態 → 各クエリ処理 → 結果出力**までの流れを可視化していきます。
2+
3+
---
4+
5+
## 🔢 問題の流れ
6+
7+
```
8+
入力:
9+
P Q
10+
q_1 Y_1
11+
q_2 Y_2
12+
...
13+
q_Q Y_Q
14+
```
15+
16+
例:
17+
18+
```
19+
998244353 3
20+
1 12
21+
2 15
22+
3 19
23+
```
24+
25+
---
26+
27+
## 🎯 前提:mod P 上の演算とは?
28+
29+
> `mod P` 上で演算とは、**P で割った余りに置き換える**操作。
30+
31+
例えば P=5 の場合:
32+
33+
| 操作式 | 通常の結果 | mod 5 結果 |
34+
| ----- | ----- | -------- |
35+
| 3 + 4 | 7 | 2 |
36+
| 3 × 4 | 12 | 2 |
37+
| 1 - 3 | -2 | 3 |
38+
39+
負の数が出る場合は、**(負数 + P) % P** で正の結果にします。
40+
41+
---
42+
43+
## 🎲 初期状態
44+
45+
```
46+
P = 998244353
47+
Q = 3
48+
X = 1 (初期値)
49+
```
50+
51+
---
52+
53+
## 🔁 クエリ処理の図解と解析
54+
55+
---
56+
57+
### ✅ クエリ1: `1 12` → X = X + 12
58+
59+
| Before | Operation | After |
60+
| ------ | ----------- | ----------- |
61+
| X = 1 | X + 12 = 13 | 13 % P = 13 |
62+
63+
📘 `1 + 12 = 13`, `13 < P` → そのまま `13`
64+
65+
🟩 **出力**:13
66+
67+
---
68+
69+
### ✅ クエリ2: `2 15` → X = X - 15
70+
71+
| Before | Operation | After |
72+
| ------ | ----------- | ------------------------ |
73+
| X = 13 | X - 15 = -2 | (-2 + P) % P = 998244351 |
74+
75+
📘 `13 - 15 = -2`, 負なので `-2 + P = 998244351`
76+
77+
🟩 **出力**:998244351
78+
79+
---
80+
81+
### ✅ クエリ3: `3 19` → X = X × 19
82+
83+
| Before | Operation | After |
84+
| ------------- | -------------------- | --------------------------- |
85+
| X = 998244351 | X × 19 = 18966642669 | 18966642669 % P = 998244315 |
86+
87+
📘 `998244351 × 19 = 18966642669`
88+
89+
```
90+
18966642669 ÷ 998244353 ≈ 18.99...
91+
18966642669 % 998244353 = 998244315
92+
```
93+
94+
🟩 **出力**:998244315
95+
96+
---
97+
98+
## 🧮 総まとめ図(各クエリの流れ)
99+
100+
```plaintext
101+
初期状態: X = 1
102+
103+
クエリ1: X = X + 12
104+
→ X = 13
105+
→ 出力: 13
106+
107+
クエリ2: X = X - 15
108+
→ X = -2 → (X + P) % P = 998244351
109+
→ 出力: 998244351
110+
111+
クエリ3: X = X × 19
112+
→ X = 998244351 × 19 = 18966642669
113+
→ X % P = 998244315
114+
→ 出力: 998244315
115+
```
116+
117+
---
118+
119+
## 🧪 性能測定のしくみ(図)
120+
121+
Node.js におけるパフォーマンス測定方法:
122+
123+
```
124+
process.hrtime.bigint() ← 高精度タイマー(ナノ秒)
125+
process.memoryUsage().rss ← 常駐メモリ(バイト単位)
126+
```
127+
128+
```plaintext
129+
処理開始 ─▶ 処理中(Q回繰り返し) ─▶ 結果出力
130+
↑ ↓
131+
startTime endTime
132+
└─────▶ 実行時間 = end - start
133+
134+
メモリ使用量 = process.memoryUsage().rss / 1024 / 1024 (MB)
135+
```
136+
137+
---
138+
139+
## 🧠 補足:なぜ BigInt を使うのか?
140+
141+
* JavaScript の `Number` 型は IEEE 754 形式の**浮動小数点数**で、**53ビットまでしか正確に扱えません**
142+
* 本問題の `P ≈ 10^9`、最大掛け算は `P × P ≈ 10^18` に近づくため、`BigInt` が必須。
143+
144+
---
145+
146+
## ✅ 結論
147+
148+
この問題は Rolling Hash の土台となる**mod 演算の性質理解と高速処理の実装練習**です。
149+
150+
### 学びポイント:
151+
152+
* `mod P` の演算:加算・減算・乗算
153+
* 負数 → 正の値に直す `(a + P) % P`
154+
* `BigInt` による精度保持
155+
* `fs``process` で高速 I/O とパフォーマンス計測
156+
157+
---
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// 以下は、Node.js (v18.16.1) と `fs` モジュールを用いて、mod P 上での加算・減算・乗算を高速に処理するコードです。
2+
// 各クエリに対する結果を出力しつつ、**処理時間**と**メモリ使用量**も出力しています。
3+
4+
// ---
5+
6+
// ### ✅ 要件整理
7+
8+
// * **演算は mod P 上**
9+
// * X の初期値は 1
10+
// * クエリ数は最大 200000
11+
// * P は 10^8 以上 2×10^9 以下の素数
12+
13+
// ---
14+
15+
// ### ✅ 実装(Node.js)
16+
17+
// ```javascript
18+
const fs = require('fs');
19+
20+
/**
21+
* 標準入力からデータを読み取り、mod P 上で加算・減算・乗算を行う
22+
* @param {string} input - 入力文字列(PとQ、その後のクエリ)
23+
* @returns {string[]} - 各クエリの結果(文字列の配列)
24+
*/
25+
function processQueries(input) {
26+
const lines = input.trim().split('\n');
27+
const [P, Q] = lines[0].split(' ').map(BigInt); // P: 素数, Q: クエリ数
28+
let X = 1n; // 初期値X
29+
const result = [];
30+
31+
for (let i = 1; i <= Q; i++) {
32+
const [q, y] = lines[i].split(' ').map(BigInt);
33+
if (q === 1n) {
34+
X = (X + y) % P;
35+
} else if (q === 2n) {
36+
X = (X - y + P) % P;
37+
} else if (q === 3n) {
38+
X = (X * y) % P;
39+
}
40+
result.push(X.toString());
41+
}
42+
43+
return result;
44+
}
45+
46+
// --- メイン処理 ---
47+
const start = process.hrtime.bigint(); // 処理時間測定開始
48+
49+
const input = fs.readFileSync('/dev/stdin', 'utf8');
50+
const result = processQueries(input);
51+
52+
// 結果を出力
53+
console.log(result.join('\n'));
54+
55+
const end = process.hrtime.bigint(); // 処理時間測定終了
56+
57+
// --- パフォーマンス出力 ---
58+
const memoryUsage = process.memoryUsage();
59+
console.error(`実行時間: ${(end - start) / 1_000_000n} ms`);
60+
console.error(`使用メモリ: ${Math.round(memoryUsage.rss / 1024 / 1024)} MB`);
61+
62+
// ### ✅ 補足
63+
64+
// * `BigInt` を使用:`P` が 10^9 を超えるため通常の `Number` では精度不足になります。
65+
// * `X = (X - Y + P) % P;` のように減算では負数対策を入れています。
66+
// * 処理時間やメモリの出力は `stderr` に出力(`console.error`)することで、標準出力に影響を与えず、ジャッジ形式にも対応可能です。
67+
68+
// ---
69+
70+
// ### ✅ 実行方法(Linux / Mac / WSL)
71+
72+
// ```bash
73+
// node solution.js < input.txt
74+
// ```
75+
76+
// ---
77+
78+
// ご希望があれば、クエリ結果をファイル出力するようにもできます。必要であれば教えてください。
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# 以下は、Python(CPython 3.11.4)での解答です。
2+
# 要求に従い、**型を明示**し、**処理時間**・**メモリ消費量**を計測し、**関数化**した構成で記述します。演算対象は **mod P 上の加算・減算・乗算**です。
3+
4+
# ---
5+
6+
# ## ✅ 実装仕様
7+
8+
# * 入力:標準入力(`sys.stdin`)
9+
# * 出力:クエリ結果(標準出力)
10+
# * 処理:`BigInteger` 型 (`int` 型で対応)
11+
# * 測定:`time`, `tracemalloc` モジュール使用
12+
13+
# ---
14+
15+
import sys
16+
import time
17+
import tracemalloc
18+
from typing import List, Tuple
19+
20+
21+
def read_input() -> Tuple[int, int, List[Tuple[int, int]]]:
22+
"""
23+
入力を標準入力から読み取り、クエリ形式に変換する。
24+
:return: P, Q, クエリのリスト (q_i, Y_i)
25+
"""
26+
input_lines = sys.stdin.read().strip().split('\n')
27+
P, Q = map(int, input_lines[0].split())
28+
queries = [tuple(map(int, line.split())) for line in input_lines[1:]]
29+
# Ensure each query is a tuple of exactly two integers
30+
queries = [(q, y) for q, y in queries]
31+
return P, Q, queries
32+
33+
34+
def process_queries(P: int, Q: int, queries: List[Tuple[int, int]]) -> List[int]:
35+
"""
36+
各クエリに対してmod P上で演算を行う。
37+
:param P: 素数 P(mod の基準)
38+
:param Q: クエリ数
39+
:param queries: クエリのリスト (q_i, Y_i)
40+
:return: 各クエリ後の x の値のリスト
41+
"""
42+
x: int = 1
43+
results: List[int] = []
44+
45+
for q, y in queries:
46+
if q == 1:
47+
x = (x + y) % P
48+
elif q == 2:
49+
x = (x - y + P) % P
50+
elif q == 3:
51+
x = (x * y) % P
52+
results.append(x)
53+
54+
return results
55+
56+
57+
def main() -> None:
58+
"""
59+
メイン関数。処理時間・メモリ計測を含む。
60+
"""
61+
# メモリ計測開始
62+
tracemalloc.start()
63+
start_time = time.perf_counter()
64+
65+
# 入力読み取り・処理
66+
P, Q, queries = read_input()
67+
results = process_queries(P, Q, queries)
68+
69+
# 出力
70+
for val in results:
71+
print(val)
72+
73+
# 処理時間とメモリ測定
74+
end_time = time.perf_counter()
75+
_, peak = tracemalloc.get_traced_memory()
76+
tracemalloc.stop()
77+
78+
# stderrに処理情報を出力(標準出力を汚さないため)
79+
print(f"処理時間: {1000 * (end_time - start_time):.3f} ms", file=sys.stderr)
80+
print(f"使用メモリ: {peak / 1024:.2f} KB", file=sys.stderr)
81+
82+
83+
if __name__ == '__main__':
84+
main()
85+
86+
### ✅ Pythonコード(解説付き)
87+
88+
# ```
89+
90+
# ---
91+
92+
# ## ✅ 出力例(入力例に対応)
93+
94+
# ```bash
95+
# $ python3 solution.py < input.txt
96+
# ```
97+
98+
# ### 入力 (`input.txt`)
99+
100+
# ```
101+
# 998244353 3
102+
# 1 12
103+
# 2 15
104+
# 3 19
105+
# ```
106+
107+
# ### 出力(標準出力)
108+
109+
# ```
110+
# 13
111+
# 998244351
112+
# 998244315
113+
# ```
114+
115+
# ### 出力(標準エラー:パフォーマンス)
116+
117+
# ```
118+
# 処理時間: 0.512 ms
119+
# 使用メモリ: 25.50 KB
120+
# ```
121+
122+
# ---
123+
124+
# ## ✅ 設計ポイント
125+
126+
# | 要素 | 内容 |
127+
# | ----- | ------------------------------------------------------ |
128+
# | 型の明示 | 引数・戻り値・変数すべてに `int`, `List[int]`, `Tuple[int, int]` 明示 |
129+
# | 時間測定 | `time.perf_counter()` 使用(ミリ秒精度) |
130+
# | メモリ測定 | `tracemalloc` によるピークメモリ使用量表示(KB単位) |
131+
# | 精度対策 | Python の `int` は任意精度整数(BigInteger)で精度問題なし |
132+
# | 安定性 | 減算時の負数補正 → `(X - y + P) % P` |
133+
134+
# ---
135+
136+
# ## ✅ 図によるクエリ解析(復習)
137+
138+
# 例:
139+
140+
# ```
141+
# X = 1
142+
# クエリ1: 1 12 → X = (1 + 12) % P = 13
143+
# クエリ2: 2 15 → X = (13 - 15 + P) % P = 998244351
144+
# クエリ3: 3 19 → X = (998244351 * 19) % P = 998244315
145+
# ```
146+
147+
# ---
148+
149+
# ## ✅ 次の学習ステップ
150+
151+
# この問題は Rolling Hash の**mod演算土台**です。次は以下の内容に進むのがおすすめです:
152+
153+
# * ✅ **mod の逆元計算**(割り算の代替手段)
154+
# * ✅ **Rolling Hash 本体実装**
155+
# * ✅ **重複部分文字列探索問題**
156+
157+
# ---
158+
159+
# 必要であれば、これらの実装・図解も対応可能です。どうしますか?

0 commit comments

Comments
 (0)