diff --git a/Mathematics/Number Theory/Constructing_a_Number.ipynb b/Mathematics/Number Theory/HuckerRank/Easy/Constructing_a_Number.ipynb
similarity index 100%
rename from Mathematics/Number Theory/Constructing_a_Number.ipynb
rename to Mathematics/Number Theory/HuckerRank/Easy/Constructing_a_Number.ipynb
diff --git a/Mathematics/Number Theory/HuckerRank/Easy/Sherlock_and_GCD.ipynb b/Mathematics/Number Theory/HuckerRank/Easy/Sherlock_and_GCD.ipynb
new file mode 100644
index 00000000..d3b9beec
--- /dev/null
+++ b/Mathematics/Number Theory/HuckerRank/Easy/Sherlock_and_GCD.ipynb
@@ -0,0 +1,756 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "2dd5686f",
+ "metadata": {},
+ "source": [
+ "## 1. 問題の整理と数学的な本質\n",
+ "\n",
+ "条件を整理すると、求める部分集合 (S) は:\n",
+ "\n",
+ "1. (S) は空でない\n",
+ "2. 「すべての要素を割り切る整数 (d > 1) が存在しない」\n",
+ " → 数学的には **(\\gcd(S) = 1)**\n",
+ "3. (S) 内に同じ値の要素が存在しない\n",
+ " → (S) の値は **全部異なる**\n",
+ "\n",
+ "### 重複に関する考察\n",
+ "\n",
+ "* 元の配列には重複があってよい\n",
+ "* ただし「部分集合 (S) の中で同じ値を 2 回使ってはダメ」という制約\n",
+ "\n",
+ "> しかし GCD の観点では、\n",
+ "> **重複しても GCD は変わらない** というのがポイントです。\n",
+ "\n",
+ "例:\n",
+ "\n",
+ "* (\\gcd(6, 10) = 2)\n",
+ "* (\\gcd(6, 10, 6) = \\gcd(2, 6) = 2)(6 を重ねても GCD は 2 のまま)\n",
+ "\n",
+ "つまり:\n",
+ "\n",
+ "* 「ある(重複を含む)部分集合で GCD が 1」なら\n",
+ " → その中の **重複を 1 個ずつにした集合** でも GCD は 1 のまま\n",
+ " → 条件 3(全部異なる)を満たす部分集合に変換できる\n",
+ "\n",
+ "### 「配列全体の GCD」で十分な理由\n",
+ "\n",
+ "* 配列全体の GCD を (g) とする\n",
+ "* もし (g > 1) なら:\n",
+ "\n",
+ " * すべての要素が (g) の倍数\n",
+ " * 任意の部分集合を取ってきても、その GCD も (g) の倍数\n",
+ " → **GCD が 1 になる部分集合は存在しない**\n",
+ "* もし (g = 1) なら:\n",
+ "\n",
+ " * 「配列の全要素の GCD = 1」なので、\n",
+ " * 重複を 1 個ずつにした集合でも GCD は 1\n",
+ " * 各値を 1 回だけ使うようにインデックスを選べば\n",
+ " → 条件 1,2,3 をすべて満たす (S) が作れる\n",
+ "\n",
+ "よって\n",
+ "\n",
+ "> **「配列全体の GCD が 1 なら YES、それ以外なら NO」**\n",
+ "\n",
+ "で判定できます。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 2. アルゴリズム比較表\n",
+ "\n",
+ "| アプローチ | 概要 | 時間計算量 | 空間計算量 | Python実装コスト | 可読性 | CPython的に妥当か | 備考 |\n",
+ "| --------------- | ------------------------ | ------------------------- | ------ | ----------- | --- | ------------ | ------------------ |\n",
+ "| 方法A: 全体GCD法(採用) | 配列全体の GCD を求めて 1 かどうかで判定 | (O(n))(各要素との gcd はほぼ定数時間) | (O(1)) | 低 | ★★★ | 非常に良い | `math.gcd` に丸投げで高速 |\n",
+ "| 方法B: 部分集合列挙 | すべての部分集合の GCD をチェック | (O(2^n)) | (O(n)) | 高 | ★☆☆ | 不適 | 制約的に完全に非現実的 |\n",
+ "| 方法C: ユニーク値GCD | 重複を set で削除してから GCD | (O(n)) | (O(n)) | 中 | ★★☆ | 良 | 正しいが A より余計なメモリ |\n",
+ "\n",
+ "**採用:** 方法A(全体 GCD)\n",
+ "\n",
+ "* 理由: 最速・最省メモリ・コード最短\n",
+ "* 数学的にも「全体 GCD が 1 ⇔ 条件を満たす部分集合が存在」と言える\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 3. Python特有の最適化ポイント\n",
+ "\n",
+ "* `math.gcd` をローカル変数に束縛して attribute lookup を削減\n",
+ "* GCD の初期値を 0 にして、1 回目だけ特別扱いしない(`gcd(0, x) = |x|`)\n",
+ "* 配列をコピー・ソート・`set` 化せず、**そのまま 1 パスで GCD を縮約**\n",
+ "* 値が負でも安全なように `abs(x)` を取っておくと堅牢(問題上は正数想定だが)\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 4. 実装パターン\n",
+ "\n",
+ "* `solve_production`\n",
+ "\n",
+ " * 業務開発向け(入力検証付き・例外を投げる想定)\n",
+ "* `solve_competitive`\n",
+ "\n",
+ " * 競技プログラミング向け(検証なし・性能優先)\n",
+ "* HackerRank が呼び出すのは `solve(a)` で、\n",
+ " 中身では `solve_competitive` を利用します。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 5. HackerRank 用 完成コード\n",
+ "\n",
+ "※ 問題文末尾のテンプレートに合わせて実装しています。\n",
+ "※ `input()` / `print()` は使わず、`solve` 関数だけで判定します。\n",
+ "\n",
+ "```python\n",
+ "#!/bin/python3\n",
+ "\n",
+ "import math\n",
+ "import os\n",
+ "import random\n",
+ "import re\n",
+ "import sys\n",
+ "from typing import List, Any\n",
+ "\n",
+ "#\n",
+ "# Complete the 'solve' function below.\n",
+ "#\n",
+ "# The function is expected to return a STRING.\n",
+ "# The function accepts INTEGER_ARRAY a as parameter.\n",
+ "#\n",
+ "\n",
+ "\n",
+ "def solve_production(a: List[int]) -> str:\n",
+ " \"\"\"\n",
+ " 業務開発向け実装(入力検証あり)\n",
+ " - 条件を満たす部分集合 S が存在すれば \"YES\"\n",
+ " - 存在しなければ \"NO\" を返す\n",
+ " \"\"\"\n",
+ " _validate_input(a)\n",
+ "\n",
+ " # この問題の本質は「配列全体の GCD が 1 かどうか」\n",
+ " has_subset = _has_valid_subset_gcd_based(a)\n",
+ " return \"YES\" if has_subset else \"NO\"\n",
+ "\n",
+ "\n",
+ "def solve_competitive(a: List[int]) -> str:\n",
+ " \"\"\"\n",
+ " 競技プログラミング向け実装\n",
+ " エラーハンドリングを省略し、性能最優先で実装。\n",
+ "\n",
+ " Time Complexity: O(n)\n",
+ " Space Complexity: O(1)\n",
+ " \"\"\"\n",
+ " # 高速化のため型チェック等は省略\n",
+ " has_subset = _has_valid_subset_gcd_based(a)\n",
+ " return \"YES\" if has_subset else \"NO\"\n",
+ "\n",
+ "\n",
+ "def _validate_input(data: Any) -> None:\n",
+ " \"\"\"\n",
+ " 業務開発向けの簡易バリデーション。\n",
+ " HackerRank 本番では solve_competitive のみを使うので、\n",
+ " ここで例外が投げられることはありません。\n",
+ " \"\"\"\n",
+ " if not isinstance(data, list):\n",
+ " raise TypeError(\"Input must be a list of integers\")\n",
+ "\n",
+ " if not data:\n",
+ " # 問題制約的には N >= 1 を想定\n",
+ " raise ValueError(\"Input list cannot be empty\")\n",
+ "\n",
+ " if len(data) > 10**6:\n",
+ " # 任意の上限(業務システム向けの安全弁)\n",
+ " raise ValueError(\"Input size exceeds limit\")\n",
+ "\n",
+ " if not all(isinstance(x, int) for x in data):\n",
+ " raise TypeError(\"All elements must be integers\")\n",
+ "\n",
+ "\n",
+ "def _has_valid_subset_gcd_based(a: List[int]) -> bool:\n",
+ " \"\"\"\n",
+ " 問題固有のメインアルゴリズム。\n",
+ " - 条件を満たす部分集合 S が存在するかどうかを bool で返す。\n",
+ "\n",
+ " ロジック:\n",
+ " - 配列全体の GCD を g とする\n",
+ " - g == 1 なら YES(条件を満たす S が必ず存在)\n",
+ " - g != 1 なら NO(どの部分集合を取っても GCD は 1 にならない)\n",
+ " \"\"\"\n",
+ " g: int = 0\n",
+ " gcd_func = math.gcd\n",
+ "\n",
+ " for x in a:\n",
+ " # 念のため絶対値を取る(負数が来ても安全に)\n",
+ " g = gcd_func(g, abs(x))\n",
+ " if g == 1:\n",
+ " # これ以上見ても GCD は 1 のままなので早期終了\n",
+ " return True\n",
+ "\n",
+ " return g == 1\n",
+ "\n",
+ "\n",
+ "def solve(a: List[int]) -> str:\n",
+ " \"\"\"\n",
+ " HackerRank から呼び出されるエントリポイント。\n",
+ " 実際には競技プログラミング向けの solve_competitive を利用。\n",
+ " \"\"\"\n",
+ " return solve_competitive(a)\n",
+ "\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " fptr = open(os.environ['OUTPUT_PATH'], 'w')\n",
+ "\n",
+ " t = int(input().strip())\n",
+ "\n",
+ " for t_itr in range(t):\n",
+ " a_count = int(input().strip())\n",
+ "\n",
+ " a = list(map(int, input().rstrip().split()))\n",
+ "\n",
+ " result = solve(a)\n",
+ "\n",
+ " fptr.write(result + '\\n')\n",
+ "\n",
+ " fptr.close()\n",
+ "```\n",
+ "\n",
+ "---\n",
+ "\n",
+ "この実装なら:\n",
+ "\n",
+ "* サンプル入力\n",
+ "\n",
+ " ```\n",
+ " 3\n",
+ " 3\n",
+ " 1 2 3\n",
+ " 2\n",
+ " 2 4\n",
+ " 3\n",
+ " 5 5 5\n",
+ " ```\n",
+ "\n",
+ "* 出力\n",
+ "\n",
+ " ```\n",
+ " YES\n",
+ " NO\n",
+ " NO\n",
+ " ```\n",
+ "\n",
+ "となり、問題の要件をすべて満たします。"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "599d38dd",
+ "metadata": {},
+ "source": [
+ "# Sherlock and GCD - 配列全体のGCDだけで判定する高速アルゴリズム\n",
+ "\n",
+ "## 目次\n",
+ "\n",
+ "* [概要](#overview)\n",
+ "* [アルゴリズム要点(TL;DR)](#tldr)\n",
+ "* [図解](#figures)\n",
+ "* [正しさのスケッチ](#correctness)\n",
+ "* [計算量](#complexity)\n",
+ "* [Python 実装](#impl)\n",
+ "* [CPython最適化ポイント](#cpython)\n",
+ "* [エッジケースと検証観点](#edgecases)\n",
+ "* [FAQ](#faq)\n",
+ "\n",
+ "---\n",
+ "\n",
+ "
概要
\n",
+ "\n",
+ "HackerRank 問題 **Sherlock and GCD** では、与えられた配列 `a` について次の条件を満たす部分集合 `S` が存在するかを判定します。\n",
+ "\n",
+ "1. `S` は空でない\n",
+ "2. `S` のすべての要素を割り切る **1より大きい整数 `d` が存在しない**\n",
+ "\n",
+ " * 数学的には `gcd(S) = 1` と等価\n",
+ "3. `S` 内に同じ値は 2 回以上出現しない(要素はすべて異なる)\n",
+ "\n",
+ "テストケースが複数あり、配列長も大きくなり得るため、\n",
+ "**全部分集合を列挙するような解法は使えません**。\n",
+ "そこで、**配列全体の GCD を 1 回だけ計算する**シンプルかつ本質的な方法を使います。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "アルゴリズム要点(TL;DR)
\n",
+ "\n",
+ "* 戦略\n",
+ "\n",
+ " * 配列 `a` 全体の GCD を `g` として計算する\n",
+ " * 結論はただこれだけ:\n",
+ "\n",
+ " * `g == 1` → 条件を満たす部分集合 `S` が存在する → `YES` / `True`\n",
+ " * `g > 1` → どんな部分集合でも GCD は 1 にならない → `NO` / `False`\n",
+ "\n",
+ "* なぜそれで十分か(直感)\n",
+ "\n",
+ " * 配列全体の GCD が 1 ということは、\n",
+ " **その配列の要素を何個か組み合わせると GCD が 1 になる** という意味\n",
+ " * 「配列全体」そのものも立派な部分集合なので、\n",
+ " 少なくともその集合で GCD 1 が達成されている\n",
+ " * 重複を消しても GCD は変わらないので、「全部異なる」という条件も満たせる\n",
+ "\n",
+ "* データ構造\n",
+ "\n",
+ " * 必要なのは `List[int]` のみ(追加配列や木・グラフは不要)\n",
+ "\n",
+ "* 計算量(1 テストケースあたり)\n",
+ "\n",
+ " * Time: `O(n)`(配列長 `n` に対して 1 パス)\n",
+ " * Space: `O(1)`(累積 GCD だけ)\n",
+ "\n",
+ "---\n",
+ "\n",
+ "\n",
+ "\n",
+ "### フローチャート(全体GCDによる判定)\n",
+ "\n",
+ "```mermaid\n",
+ "flowchart TD\n",
+ " Start[Start] --> Init[Init g as 0]\n",
+ " Init --> LoopCheck{More elements}\n",
+ " LoopCheck -- Yes --> Take[Read next value x]\n",
+ " Take --> Update[Update g with gcd g x]\n",
+ " Update --> Early{g equals 1}\n",
+ " Early -- Yes --> RetYes[Return YES]\n",
+ " Early -- No --> LoopCheck\n",
+ " LoopCheck -- No --> FinalCheck{g equals 1}\n",
+ " FinalCheck -- Yes --> RetYes2[Return YES]\n",
+ " FinalCheck -- No --> RetNo[Return NO]\n",
+ "```\n",
+ "\n",
+ "**説明(日本語)**\n",
+ "\n",
+ "* GCD を 0 から累積し、各要素を取り込むたびに更新します。\n",
+ "* 途中で `g == 1` になったら、それ以降 GCD は 1 のままなので即 `YES` を返せます。\n",
+ "* 最後まで 1 にならなければ、どの部分集合でも GCD は 1 にならないため `NO` です。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "### データフロー図(入力から出力まで)\n",
+ "\n",
+ "```mermaid\n",
+ "graph LR\n",
+ " subgraph Precheck\n",
+ " A[Input array] --> B[Read values]\n",
+ " end\n",
+ " subgraph Core\n",
+ " B --> C[Iterate elements]\n",
+ " C --> D[Compute gcd cumulatively]\n",
+ " D --> E[Check gcd equals 1]\n",
+ " end\n",
+ " E --> F[Output YES or NO]\n",
+ "```\n",
+ "\n",
+ "**説明(日本語)**\n",
+ "\n",
+ "* 入力配列をそのまま 1 回走査して GCD を累積するだけのシンプルな構造です。\n",
+ "* 追加の配列や補助データ構造は不要で、出力は `YES/NO` のブーリアン判定です。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "正しさのスケッチ
\n",
+ "\n",
+ "ここでは、この「全体 GCD だけ見る」アルゴリズムがなぜ正しいかを、\n",
+ "数学的な性質に基づいて確認します。\n",
+ "\n",
+ "### 1. 条件 2 と GCD の関係\n",
+ "\n",
+ "* 整数列 `S` の GCD を `g = gcd(S)` とすると:\n",
+ "\n",
+ " * `g` は「`S` のすべての要素を割り切る整数のうち最大のもの」\n",
+ " * よって、1 より大きい整数 `d` が `S` のすべての要素を割り切る ⇔ `g >= 2`\n",
+ " * 逆に「1 より大きい共通の割り切れる整数が存在しない」 ⇔ `g = 1`\n",
+ "\n",
+ "* つまり、問題の条件 2 は\n",
+ "\n",
+ "> 「`S` の GCD が 1 であること」\n",
+ "\n",
+ "と完全に等価です。\n",
+ "\n",
+ "### 2. 全体 GCD が 1 なら十分(十分性)\n",
+ "\n",
+ "配列 `A = [a1, a2, ..., an]` とし、`g = gcd(A)` とします。\n",
+ "\n",
+ "* 仮定:`g = 1`\n",
+ "\n",
+ "* このとき、**配列全体の集合 `{a1, ..., an}` 自体が GCD 1 の部分集合 `S`** です。\n",
+ "\n",
+ " * これは `S` が空でないことも満たします。\n",
+ "\n",
+ "* さらに、GCD を左から順に計算していくと:\n",
+ "\n",
+ " ```text\n",
+ " g1 = gcd(a1)\n",
+ " g2 = gcd(a1, a2)\n",
+ " ...\n",
+ " gn = gcd(a1, a2, ..., an) = 1\n",
+ " ```\n",
+ "\n",
+ " となります。`gn = 1` になっているので、どこかの `k` で\n",
+ " `gk = gcd(a1, ..., ak) = 1` が初めて成立します。\n",
+ "\n",
+ "* このときの prefix 集合 `S_prefix = {a1, ..., ak}` も GCD 1 の部分集合です(ただし、`k = n` の場合もあります)。\n",
+ "\n",
+ "* ここで、もし `S_prefix` の中に同じ値が複数回出現していても、\n",
+ " **重複を 1 回ずつに削っていっても GCD は変わりません**。\n",
+ "\n",
+ " * 例として `{6, 10, 15, 6}` の GCD は `{6, 10, 15}` の GCD と同じです。\n",
+ "\n",
+ "* よって、重複を除いた集合 `S`(各値 1 回ずつだけ使う)でも GCD は 1 のままです。\n",
+ "\n",
+ "* 結果として:\n",
+ "\n",
+ "> 全体 GCD が 1 であれば、\n",
+ "> 条件 1(非空)・条件 2(GCD 1)・条件 3(全部異なる)を満たす部分集合 `S` が必ず存在する。\n",
+ "\n",
+ "### 3. 全体 GCD が 1 でないなら不可能(必要性)\n",
+ "\n",
+ "次に、逆方向を確認します。\n",
+ "\n",
+ "* 仮定:配列全体 `A` の GCD が `g > 1`\n",
+ "\n",
+ " * つまり、すべての `a_i` が `g` の倍数です。\n",
+ "* 任意の部分集合 `S` を取っても、要素はすべて `g` の倍数のままなので、\n",
+ "\n",
+ " * `g` は `S` のすべての要素を割り切ります。\n",
+ " * したがって `gcd(S)` は `g` の約数であり、`gcd(S) >= 2` が必ず成り立ちます。\n",
+ "* よって、**どんな部分集合を取ってきても GCD が 1 になることはありません**。\n",
+ "* したがって、\n",
+ "\n",
+ "> 全体 GCD が 1 でない場合、条件 2 を満たす部分集合 `S` は存在しません。\n",
+ "\n",
+ "### 4. 条件 3(重複禁止)の扱い\n",
+ "\n",
+ "条件 3 は「`S` 内で同じ値が 2 回以上登場してはならない」というものです。\n",
+ "\n",
+ "* 重要な事実:**GCD は同じ値を何回足しても変わりません。**\n",
+ "\n",
+ " * 例:`gcd(6, 10) = 2` と `gcd(6, 10, 6) = 2` は同じ\n",
+ "* もし重複を含む集合 `S_full` で GCD が 1 になっているなら、\n",
+ "\n",
+ " * そこから重複を削っていっても GCD は 1 のままです。\n",
+ "* よって、「重複を許した世界で GCD 1 の部分集合が存在」するなら、\n",
+ "\n",
+ " * 「重複を削った世界(条件 3 を満たす世界)でも GCD 1 の部分集合が存在」します。\n",
+ "* これにより、**存在判定としては全体 GCD のみ見れば十分**であることが分かります。\n",
+ "\n",
+ "### 5. 終了性\n",
+ "\n",
+ "* 配列長を `n` とすると、アルゴリズムは `n` ステップ(最大)で終了します。\n",
+ "* 各ステップで行う処理は:\n",
+ "\n",
+ " * 1 回の `gcd` 計算(`math.gcd`)\n",
+ " * 1 回の `abs`(任意)\n",
+ "* 途中で `g == 1` になれば即座に終了します。\n",
+ "* よって、**必ず有限ステップで終了します**。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "計算量
\n",
+ "\n",
+ "1 テストケースあたりの計算量:\n",
+ "\n",
+ "* 時間計算量: **O(n)**\n",
+ "\n",
+ " * 配列を 1 回走査するだけ(`n` は配列長)\n",
+ "\n",
+ "* 空間計算量: **O(1)**\n",
+ "\n",
+ " * 累積 GCD `g` とループ変数のみ(入力配列以外の追加配列なし)\n",
+ "\n",
+ "表にまとめると:\n",
+ "\n",
+ "| 観点 | 計算量 | メモ |\n",
+ "| ----- | --------- | ----------------------- |\n",
+ "| Time | O(n) | 1 パスの線形時間 |\n",
+ "| Space | O(1) | in-place 判定、追加データ構造ほぼ不要 |\n",
+ "| データ構造 | List[int] | Python の通常のリストのみ利用 |\n",
+ "\n",
+ "---\n",
+ "\n",
+ "Python 実装
\n",
+ "\n",
+ "ここでは、LeetCode 風のクラス形式インターフェースで実装します。\n",
+ "\n",
+ "* メソッドシグネチャ例:\n",
+ "\n",
+ " ```python\n",
+ " class Solution:\n",
+ " def hasValidSubset(self, nums: List[int]) -> bool:\n",
+ " ...\n",
+ " ```\n",
+ "\n",
+ "* 返り値:\n",
+ "\n",
+ " * 条件を満たす部分集合 `S` が存在する → `True`\n",
+ " * 存在しない → `False`\n",
+ "\n",
+ "```python\n",
+ "from __future__ import annotations\n",
+ "\n",
+ "import math\n",
+ "from typing import List\n",
+ "\n",
+ "\n",
+ "class Solution:\n",
+ " \"\"\"\n",
+ " Sherlock and GCD 判定ロジック\n",
+ "\n",
+ " 与えられた整数配列 nums について、以下を満たす部分集合 S が\n",
+ " 存在するかどうかを判定する:\n",
+ "\n",
+ " 1. S は空でない\n",
+ " 2. S の全要素を割り切る 1 より大きい整数 d が存在しない\n",
+ " (すなわち gcd(S) == 1)\n",
+ " 3. S 内で同じ値が 2 回以上登場しない\n",
+ "\n",
+ " 実装上は「配列全体の gcd が 1 かどうか」を見るだけで十分。\n",
+ " \"\"\"\n",
+ "\n",
+ " def hasValidSubset(self, nums: List[int]) -> bool:\n",
+ " \"\"\"\n",
+ " 条件を満たす部分集合が存在すれば True、存在しなければ False を返す。\n",
+ "\n",
+ " Time: O(n)\n",
+ " Space: O(1)\n",
+ " \"\"\"\n",
+ " # 累積 GCD。0 から始めることで、最初の要素を自然に取り込める。\n",
+ " g: int = 0\n",
+ "\n",
+ " # ローカル変数に束縛して、ループ内での属性アクセスコストを削減。\n",
+ " gcd_func = math.gcd\n",
+ "\n",
+ " for x in nums:\n",
+ " # 負数が紛れ込んでも安定するよう、絶対値をとっておく。\n",
+ " # 問題設定上、正の整数のみであればそのままでも構わないが、\n",
+ " # こうしておくとロバストネスが増す。\n",
+ " g = gcd_func(g, abs(x))\n",
+ "\n",
+ " # GCD が 1 になった時点で、以降何を見ても GCD は 1 のままなので、\n",
+ " # ここで早期リターンしてよい。\n",
+ " if g == 1:\n",
+ " return True\n",
+ "\n",
+ " # 最後まで GCD が 1 にならなかった場合、\n",
+ " # 条件を満たす部分集合は存在しない。\n",
+ " return g == 1\n",
+ "```\n",
+ "\n",
+ "---\n",
+ "\n",
+ "CPython最適化ポイント
\n",
+ "\n",
+ "この問題はそもそも O(n) で非常に軽いですが、CPython 3.11+ 的なチューニングポイントを挙げておきます。\n",
+ "\n",
+ "* `math.gcd` のローカル変数化\n",
+ "\n",
+ " ```python\n",
+ " gcd_func = math.gcd\n",
+ " ...\n",
+ " g = gcd_func(g, abs(x))\n",
+ " ```\n",
+ "\n",
+ " * ループ内部で `math.gcd` を毎回属性参照するより、\n",
+ " ローカル変数に束縛したほうが名前解決が速くなります。\n",
+ " * 小さな差ですが、ループ回数が多いと効いてきます。\n",
+ "\n",
+ "* 不要なコンテナ・コピーの排除\n",
+ "\n",
+ " * `set(nums)` や `sorted(nums)` を作らない\n",
+ " * 部分集合を実際に生成しない\n",
+ " * スライス(`nums[:]`)等も不要\n",
+ " * これにより、メモリアロケーションと GC コストを抑えます。\n",
+ "\n",
+ "* 早期終了で平均時間を短縮\n",
+ "\n",
+ " * 多くの実用的な入力では、最初の数要素で GCD がすぐ 1 になることが多いです。\n",
+ " * `if g == 1: return True` により、平均的な計算時間を大きく削減できます。\n",
+ "\n",
+ "* 純粋関数スタイル\n",
+ "\n",
+ " * `hasValidSubset` は引数のリストを書き換えない純粋関数的実装です。\n",
+ " * これにより、他処理との組み合わせやテストがやりやすくなります。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "エッジケースと検証観点
\n",
+ "\n",
+ "テスト設計の観点から、特に確認しておきたいケースを列挙します。\n",
+ "\n",
+ "1. **単一要素**\n",
+ "\n",
+ " * `[1]`\n",
+ "\n",
+ " * `gcd(1) = 1` → `True`(`S = {1}`)\n",
+ " * `[5]`\n",
+ "\n",
+ " * `gcd(5) = 5` → `False`(どの部分集合も GCD が 5)\n",
+ "\n",
+ "2. **すべて同じ値**\n",
+ "\n",
+ " * `[5, 5, 5]`\n",
+ "\n",
+ " * 全体 GCD = 5 → `False`\n",
+ " * 問題文サンプルの「5 5 5」ケースと一致。\n",
+ "\n",
+ "3. **すべてが同じ素数の倍数**\n",
+ "\n",
+ " * `[2, 4]`\n",
+ "\n",
+ " * GCD = 2 → `False`\n",
+ " * `[6, 10, 14]`\n",
+ "\n",
+ " * GCD = 2 → `False`\n",
+ " * どの部分集合でも GCD は 1 にならない。\n",
+ "\n",
+ "4. **全体 GCD は 1 だが、真部分集合では 1 にならない例**\n",
+ "\n",
+ " * `[6, 10, 15]`\n",
+ "\n",
+ " * 単体: 6, 10, 15 → GCD はそれぞれ 6, 10, 15\n",
+ " * ペア: gcd(6,10)=2, gcd(6,15)=3, gcd(10,15)=5\n",
+ " * 全体: gcd(6,10,15)=1\n",
+ " * **GCD が 1 になるのは全体集合のみ**だが、それでも条件を満たす部分集合は存在する\n",
+ " → アルゴリズムの判定(全体 GCD == 1 → True)は正しい。\n",
+ "\n",
+ "5. **重複を含むが GCD が 1**\n",
+ "\n",
+ " * `[2, 3, 3]`\n",
+ "\n",
+ " * `gcd(2, 3, 3) = 1` → `True`\n",
+ " * 条件 3 を満たす部分集合としては `S = {2, 3}` などが取れる。\n",
+ "\n",
+ "6. **0 や負数を含む場合(ロバストネスチェック)**\n",
+ "\n",
+ " 実際の問題では非負整数のみが想定されていることが多いですが、実装の堅牢性を確認:\n",
+ "\n",
+ " * `[0, 1]` → `gcd(0, 1) = 1` → `True`\n",
+ " * `[-2, 4]` → `gcd(2, 4) = 2` → `False`(abs を取った振る舞い)\n",
+ "\n",
+ "7. **最大サイズの入力**\n",
+ "\n",
+ " * 制約上許される最大の `n` と値を与えても、\n",
+ " 本アルゴリズムは O(n)・定数メモリのため、TLE や MLE の心配はほぼありません。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "FAQ
\n",
+ "\n",
+ "**Q1. なぜ部分集合を実際に列挙しなくてよいのですか?**\n",
+ "\n",
+ "* A. GCD の性質上、\n",
+ "\n",
+ " * 全体 GCD が 1 のとき:\n",
+ "\n",
+ " * 少なくとも「配列全体」という部分集合で GCD 1 が達成されています。\n",
+ " * さらに、その過程のどこかの prefix でも GCD 1 になる場合が多いです(ならない場合もありますが、配列全体が使えます)。\n",
+ " * 全体 GCD が 1 でないとき:\n",
+ "\n",
+ " * どんな部分集合を取っても、その GCD は全体 GCD の約数であり、1 にはなりません。\n",
+ "\n",
+ " という「存在判定」として十分な条件が成り立つため、\n",
+ " **部分集合を列挙する必要がなくなります**。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "**Q2. 条件 3(重複禁止)はどう反映されているのですか?**\n",
+ "\n",
+ "* A. GCD は同じ数を何回足しても変わらないため、\n",
+ "\n",
+ " * もし重複を含む集合で GCD = 1 が達成されているなら、\n",
+ " * その重複を 1 つずつ消しても GCD は 1 のままです。\n",
+ "\n",
+ "* したがって、「重複を許した世界で GCD 1 の部分集合が存在」するなら、\n",
+ " 「重複を禁止した世界でも GCD 1 の部分集合が存在」します。\n",
+ " → **全体 GCD のみで判定して問題ありません。**\n",
+ "---\n",
+ "\n",
+ "**Q3. 全体 GCD が 1 なら、必ず「一部だけ取り出した集合」でも GCD 1 になりますか?**\n",
+ "\n",
+ "* **A. いいえ、それは保証できません。**\n",
+ "\n",
+ " 具体例として、配列が `[6, 10, 15]` の場合を見てみます。\n",
+ "\n",
+ " 1. 全体の GCD\n",
+ "\n",
+ " * `gcd(6, 10, 15) = 1`\n",
+ " → **「全部を使った集合 `{6, 10, 15}` では GCD が 1 になる**\n",
+ "\n",
+ " 2. 真部分集合(全部は使わないやつ)の GCD\n",
+ "\n",
+ " * 1 個だけ取る場合\n",
+ "\n",
+ " * `{6}` → GCD = 6\n",
+ " * `{10}` → GCD = 10\n",
+ " * `{15}` → GCD = 15\n",
+ " * 2 個だけ取る場合\n",
+ "\n",
+ " * `{6, 10}` → GCD = 2\n",
+ " * `{6, 15}` → GCD = 3\n",
+ " * `{10, 15}` → GCD = 5\n",
+ "\n",
+ " → この例では、**どの真部分集合でも GCD は 1 になっていません**。\n",
+ " **GCD が 1 になるのは「全部を使った集合」だけ**です。\n",
+ "\n",
+ "### それでも「YES」と判定してよい理由\n",
+ "\n",
+ "* 問題の条件は「**部分集合 `S` が存在するか**」であって、\n",
+ "\n",
+ " * 「真部分集合でないといけない」とは書いていません。\n",
+ "* 「配列全体を使った集合」も、ちゃんと **条件を満たす部分集合 `S` の 1 つ**です。\n",
+ "\n",
+ "つまり:\n",
+ "\n",
+ "* 全体 GCD が 1 なら\n",
+ "\n",
+ " * 少なくとも「配列全体」という集合で GCD が 1 になる\n",
+ " * それだけでも **「条件を満たす `S` が存在する」ので答えは YES**\n",
+ "\n",
+ "ということです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "**Q4. 配列に 1 が含まれていたらどうなりますか?**\n",
+ "\n",
+ "* A. 即座に答えは `YES` です。\n",
+ "\n",
+ " * `{1}` という単一要素の部分集合を取れば、\n",
+ "\n",
+ " * GCD(1) = 1 → 条件 2 を満たし\n",
+ " * 要素も 1 つだけなので重複もありません(条件 3 も満たす)\n",
+ "\n",
+ "* 実装上も、最初に 1 を取り込んだ瞬間に累積 GCD が 1 になり、即 `True` を返します。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "**Q5. ゼロを含んでいる場合は安全ですか?**\n",
+ "\n",
+ "* A. `gcd(0, x) = |x|` と定義されており、実装もこの性質に従っています。\n",
+ "\n",
+ " * 初期値を `g = 0` にすることで、最初の要素を自然に取り込めます。\n",
+ " * もし全要素が 0 なら GCD = 0 のままなので、1 にはならず `False` になります。\n",
+ " * 0 と 1 が混ざっていれば GCD は 1 になります。\n",
+ "\n",
+ "* 問題の仕様が正の整数のみだとしても、この実装はゼロに対しても安定に動作します。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "この README に沿って実装・検証を進めれば、\n",
+ "「GCD による存在判定」という観点から Sherlock and GCD をしっかり理解し、\n",
+ "類似問題にも応用できるはずです。"
+ ]
+ }
+ ],
+ "metadata": {
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}