Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0805330f",
"metadata": {},
"source": [
"# Difference and Product - 絶対差と積で数える整数対\n",
"\n",
"* **プラットフォーム**: HackerRank\n",
"* **問題ID**: Difference and Product\n",
"\n",
"---\n",
"\n",
"## 目次\n",
"\n",
"* [概要](#overview)\n",
"* [アルゴリズム要点 (TL;DR)](#tldr)\n",
"* [図解](#figures)\n",
"* [証明のスケッチ](#proof)\n",
"* [計算量](#complexity)\n",
"* [Python 実装](#impl)\n",
"* [CPython 最適化ポイント](#cpython)\n",
"* [エッジケースと検証](#edgecases)\n",
"* [FAQ](#faq)\n",
"\n",
"---\n",
"\n",
"<h2 id=\"overview\">概要</h2>\n",
"\n",
"* **問題要約**: 整数 $d$ と $p$ が与えられる。$|x-y|=d$ かつ $xy=p$ を満たす**有序**整数対 $(x,y)$ の個数を求める。\n",
"* **入出力仕様(簡潔)**:\n",
"\n",
" * 入力: $T$ テスト、各行に $d\\ p$\n",
" * 出力: 各テストについて解の個数(整数)\n",
"* **関数シグネチャ(HackerRank 準拠・必須)**: `solve(d: int, p: int) -> int`\n",
"* **想定データ構造**: 単一の整数演算のみ(補助配列不要)\n",
"* **代表例**:\n",
"\n",
" * $d=1,\\ p=2 \\Rightarrow 4$\n",
" * $d=0,\\ p=4 \\Rightarrow 2$\n",
" * $d=-1,\\ p=1 \\Rightarrow 0$\n",
"\n",
"---\n",
"\n",
"<h2 id=\"tldr\">アルゴリズム要点 (TL;DR)</h2>\n",
"\n",
"* 和差変数を導入する:$s=x+y,\\ t=x-y$。すると\n",
" $$\n",
" |t|=d \\quad\\text{かつ}\\quad xy=\\frac{s^2-t^2}{4}=p \\ \\Rightarrow\\ s^2=4p+d^2\n",
" $$\n",
"* $N=4p+d^2$ とおく。$N$ が**完全平方**でなければ解は $0$。\n",
"* $q=\\sqrt{N}$(整数)とすると、$s\\in{\\pm q}$、$t\\in{d,-d}$。\n",
"\n",
" * $q>0$ なら $s$ は $2$ 通り、$q=0$ なら $1$ 通り。\n",
" * $d>0$ なら $t$ は $2$ 通り、$d=0$ なら $1$ 通り。\n",
"* よって個数は\n",
" $$\n",
" \\bigl(1 + \\mathbf{1}*{q>0}\\bigr)\\times \\bigl(1 + \\mathbf{1}*{d>0}\\bigr)\n",
" $$\n",
"* ただし $d<0$ は $|x-y|=d$ を満たせないため常に $0$。\n",
"* **計算量**: 時間 $O(1)$、空間 $O(1)$(`math.isqrt` による完全平方判定)。\n",
"\n",
"式変形と「通り数の数え方」が腑に落ちるよう、ステップごとに具体化します。数式は KaTeX で再掲し、ミニ例も置きます。\n",
"\n",
"---\n",
"\n",
"## 補足解説:TL;DR の具体化\n",
"\n",
"### 1) なぜ和差変数にするのか\n",
"\n",
"元の条件は\n",
"\n",
"* 差の条件:$|x-y|=d$\n",
"* 積の条件:$xy=p$\n",
"\n",
"これを\n",
"$$\n",
"s=x+y,\\quad t=x-y\n",
"$$\n",
"に置き換えると、$x$ と $y$ を $s,t$ から\n",
"$$\n",
"x=\\frac{s+t}{2},\\quad y=\\frac{s-t}{2}\n",
"$$\n",
"で一意に復元できます(順序付きなので $(s,t)$ が変われば一般に $(x,y)$ も変わる)。\n",
"\n",
"差の条件はただちに $|t|=d$、つまり $t\\in{d,-d}$ と書けます。\n",
"\n",
"積の条件は\n",
"$$\n",
"xy=\\frac{(x+y)^2-(x-y)^2}{4}=\\frac{s^2-t^2}{4}=p\n",
"$$\n",
"で、これを $s$ について解けば\n",
"$$\n",
"s^2=t^2+4p\n",
"$$\n",
"ここで $|t|=d$ を代入すると\n",
"$$\n",
"s^2=4p+d^2\n",
"$$\n",
"\n",
"### 2) 「完全平方」でなければ即 0\n",
"\n",
"$$\n",
"N = 4p+d^2\n",
"$$\n",
"と置くと、上の式は $s^2=N$。$s$ は整数なので、$N$ が**完全平方**でなければ整数 $s$ は存在せず、解は $0$ です。\n",
"(補足:$N<0$ のときも当然 $s^2=N$ は不可能なので $0$。)\n",
"\n",
"### 3) $s$ と $t$ の候補数を掛け合わせるだけ\n",
"\n",
"$N$ が完全平方なら $q=\\sqrt{N}\\in\\mathbb{Z}$ が存在します。このとき\n",
"$$\n",
"s\\in{+q,-q},\\quad t\\in{d,-d}.\n",
"$$\n",
"\n",
"* $q>0$ なら $s$ は ${+q,-q}$ の **2 通り**。\n",
"* $q=0$ なら $s=0$ の **1 通り**($+0$ と $-0$ は同じ)。\n",
"* $d>0$ なら $t$ は ${+d,-d}$ の **2 通り**。\n",
"* $d=0$ なら $t=0$ の **1 通り**(絶対値が 0 のときは符号の分岐がありません)。\n",
"\n",
"よって通り数は\n",
"$$\n",
"\\bigl(1+\\mathbf{1}*{q>0}\\bigr)\\times\\bigl(1+\\mathbf{1}*{d>0}\\bigr),\n",
"$$\n",
"つまり\n",
"\n",
"* $q>0$ なら $s$ 側が $2$、$q=0$ なら $1$\n",
"* $d>0$ なら $t$ 側が $2$、$d=0$ なら $1$\n",
" を掛け合わせた個数になります。\n",
"\n",
"> 注:$d<0$ の入力は、そもそも $|x-y|=d$ を満たせないので **常に 0**。\n",
"\n",
"### 4) 偶奇(パリティ)の心配は要らない理由\n",
"\n",
"$x=\\dfrac{s+t}{2},,y=\\dfrac{s-t}{2}$ が整数になるには、$s$ と $t$ の偶奇が一致している必要があります。\n",
"ここで $t=\\pm d$、$s^2=4p+d^2$ より\n",
"$$\n",
"s^2 \\equiv d^2 \\pmod{4}\n",
"$$\n",
"が成り立つので、$s$ と $d$ は同じ偶奇になります。よって $t=\\pm d$ とも偶奇一致し、$x,y$ は必ず整数になります。\n",
"(完全平方を満たした時点で偶奇条件は自動で通ります。)\n",
"\n",
"---\n",
"\n",
"## ミニ例で確認\n",
"\n",
"### 例1:$d=1,\\ p=2$\n",
"\n",
"* $N=4p+d^2=8+1=9$ は完全平方、$q=3>0$。\n",
"* $s\\in{+3,-3}$(2 通り)、$t\\in{+1,-1}$(2 通り)で合計 $2\\times 2=4$。\n",
"* 復元してみると\n",
" $$\n",
" (s,t)=(3,1)\\Rightarrow (x,y)=\\Bigl(\\frac{3+1}{2},\\frac{3-1}{2}\\Bigr)=(2,1) \\\n",
" (3,-1)\\Rightarrow (2,-1) \\\n",
" (-3,1)\\Rightarrow (-1,-2) \\\n",
" (-3,-1)\\Rightarrow (-2,-1)\n",
" $$\n",
" で確かに 4 通り。\n",
"\n",
"### 例2:$d=0,\\ p=4$\n",
"\n",
"* $N=4p+d^2=16$、$q=4>0$。\n",
"* $s$ は 2 通り($\\pm 4$)、$t$ は 1 通り($0$)なので合計 $2\\times 1=2$。\n",
"* 復元:$(s,t)=(4,0)\\Rightarrow (2,2)$、$(-4,0)\\Rightarrow (-2,-2)$。\n",
"\n",
"### 例3:$d=-1,\\ p=1$\n",
"\n",
"* $d<0$ は不可能なので即 $0$。\n",
"\n",
"---\n",
"\n",
"## つまずきポイント早見\n",
"\n",
"* **$q=0$ を 2 通りと数えない**:$s=\\pm 0$ は同一。\n",
"* **$d=0$ を 2 通りと数えない**:$t=\\pm 0$ は同一。\n",
"* **完全平方チェックだけで十分**:満たせば偶奇も自動で揃う。\n",
"* **順序対であることに注意**:$t$ の符号違いで $(x,y)$ の順序が入れ替わるため、$d>0$ なら $t$ 側で 2 通りが立つ。\n",
"\n",
"これで、TL;DR の各 bullet が「なぜそう数えるか」までクリアになるはずです。\n",
"\n",
"---\n",
"\n",
"<h2 id=\"figures\">図解</h2>\n",
"\n",
"**フローチャート(式変形の流れ)**\n",
"\n",
"```mermaid\n",
"flowchart TD\n",
" Start[開始]\n",
" Eq1[\"和差の導入 s=x+y, t=x-y\"]\n",
" Transform[\"積の式変形で s^2=4p+d^2\"]\n",
" CheckSquare[N が完全平方か判定]\n",
" Count[s,t の通り数を積]\n",
" Output[個数を出力]\n",
"\n",
" Start --> Eq1\n",
" Eq1 --> Transform\n",
" Transform --> CheckSquare\n",
" CheckSquare --> Count\n",
" Count --> Output\n",
"```\n",
"\n",
"*説明*: 和差変数で二次式に落としてから、完全平方かどうかのみを判定し、$s$ と $t$ の選択肢の積で個数を得る。\n",
"\n",
"**データフロー(入力から結果まで)**\n",
"\n",
"```mermaid\n",
"graph LR\n",
" A[\"入力 d,p\"]\n",
" B[\"N=4p+d^2\"]\n",
" C[\"q=isqrt(N)\"]\n",
" D[完全平方チェック]\n",
" E[個数計算]\n",
" F[出力]\n",
"\n",
" A --> B\n",
" B --> C\n",
" C --> D\n",
" D --> E\n",
" E --> F\n",
"```\n",
"\n",
"*説明*: $N$ の計算と整数平方根での完全平方判定を経て、通り数の直積で答えを得る。\n",
"\n",
"---\n",
"\n",
"<h2 id=\"proof\">証明のスケッチ</h2>\n",
"\n",
"* **設定**: $s=x+y,\\ t=x-y$ とする。$|t|=d$ より $t\\in{d,-d}$。\n",
"* **主要式**: $xy=\\dfrac{s^2-t^2}{4}=p$ より $s^2=4p+d^2$。\n",
"* **必要十分性**:\n",
"\n",
" * $N=4p+d^2$ が完全平方であれば $q=\\sqrt{N}\\in\\mathbb{Z}$ が存在し、$s\\in{\\pm q}$ を取れる。\n",
" * $t\\in{d,-d}$ と組にすれば\n",
" $$\n",
" x=\\frac{s+t}{2},\\quad y=\\frac{s-t}{2}\n",
" $$\n",
" は整数となる($s^2\\equiv d^2 \\pmod{4}$ が成り立ち、$s$ と $d$ の偶奇が一致するため)。\n",
" * 逆に $N$ が完全平方でなければ整数 $s$ は存在せず、よって解は存在しない。\n",
"* **計数**:\n",
"\n",
" * $q>0$ のとき $s$ は $2$ 通り、$q=0$ のとき $1$ 通り。\n",
" * $d>0$ のとき $t$ は $2$ 通り、$d=0$ のとき $1$ 通り。\n",
" * よって個数は $\\left(1+\\mathbf{1}*{q>0}\\right)\\left(1+\\mathbf{1}*{d>0}\\right)$。\n",
"* **基底ケース**:\n",
"\n",
" * $d<0$ は $|x-y|=d$ が不可能なので $0$。\n",
" * $d=0,\\ p=0$ では $N=0$、$q=0$、$s=0,\\ t=0$ の一組 $(0,0)$ のみで $1$。\n",
"* **終了性**: 各判定は有限個の整数演算のみで完了する。\n",
"\n",
"---\n",
"\n",
"<h2 id=\"complexity\">計算量</h2>\n",
"\n",
"* **時間計算量**: $O(1)$\n",
"* **空間計算量**: $O(1)$\n",
"\n",
"---\n",
"\n",
"<h2 id=\"impl\">Python 実装</h2>\n",
"\n",
"> 注: HackerRank では関数ベースで採点されます。下記は **CPython 3.13.3** 想定、型注釈付き、純粋関数です。スタブ(入出力処理)はプラットフォーム側に従ってください。\n",
"\n",
"```python\n",
"from __future__ import annotations\n",
"from typing import Final\n",
"import math\n",
"\n",
"def solve(d: int, p: int) -> int:\n",
" \"\"\"\n",
" |x - y| = d かつ x*y = p を満たす有序整数対 (x,y) の個数を返す。\n",
"\n",
" 数式対応(本文の KaTeX と対応):\n",
" - s = x + y, t = x - y\n",
" - |t| = d\n",
" - x*y = (s^2 - t^2)/4 = p より s^2 = 4p + d^2\n",
" - N = 4p + d^2\n",
" - N が完全平方で q = sqrt(N) が整数なら:\n",
" s は {+q, -q}(q>0 で2通り、q=0で1通り)\n",
" t は {+d, -d}(d>0で2通り、d=0で1通り)\n",
" 個数 = (1 if q==0 else 2) * (1 if d==0 else 2)\n",
" - d < 0 のときは |x - y| = d を満たせないので 0\n",
" \"\"\"\n",
" # |x - y| = d は d >= 0 が必須\n",
" if d < 0:\n",
" return 0\n",
"\n",
" # N = 4p + d^2\n",
" N: Final[int] = 4 * p + d * d\n",
" if N < 0:\n",
" # 4p が負で d^2 を足しても負になる極端なケース(p が十分に負)\n",
" return 0\n",
"\n",
" # 完全平方判定: math.isqrt は丸め誤差なしの整数平方根\n",
" q: int = math.isqrt(N)\n",
" if q * q != N:\n",
" return 0\n",
"\n",
" s_count: int = 1 if q == 0 else 2\n",
" t_count: int = 1 if d == 0 else 2\n",
" return s_count * t_count\n",
"```\n",
"\n",
"---\n",
"\n",
"<h2 id=\"cpython\">CPython 最適化ポイント</h2>\n",
"\n",
"* **`math.isqrt`**: 整数の平方根を誤差なく返し、完全平方判定に最適。\n",
"* **任意精度整数**: Python の `int` は任意精度でオーバーフローを気にしない。\n",
"* **分岐の最小化**: 判定順序を $d<0 \\Rightarrow N<0 \\Rightarrow$ 完全平方 の順にして早期リターン。\n",
"\n",
"---\n",
"\n",
"<h2 id=\"edgecases\">エッジケースと検証</h2>\n",
"\n",
"* $d<0$ $\\Rightarrow$ $0$。\n",
"* $N=4p+d^2<0$(大きく負の $p$)$\\Rightarrow$ $0$。\n",
"* $N$ が完全平方でない $\\Rightarrow$ $0$。\n",
"* $d=0$:\n",
"\n",
" * $N=4p$ が完全平方なら $q=2\\sqrt{|p|}$ が整数のとき $s$ は $2$ 通り(ただし $q=0$ なら $1$ 通り)、$t$ は $1$ 通り。\n",
" * 例: $d=0,\\ p=4 \\Rightarrow N=16,\\ q=4 \\Rightarrow 2$。\n",
"* $d=0,\\ p=0$:\n",
"\n",
" * $N=0,\\ q=0 \\Rightarrow 1$($(0,0)$ のみ)。\n",
"* 代表例との整合:\n",
"\n",
" * $d=1,\\ p=2$: $N=9,\\ q=3>0 \\Rightarrow (2)\\times(2)=4$。\n",
" * $d=0,\\ p=4$: $N=16,\\ q=4>0 \\Rightarrow (2)\\times(1)=2$。\n",
" * $d=-1,\\ p=1$: $d<0 \\Rightarrow 0$。\n",
"\n",
"---\n",
"\n",
"<h2 id=\"faq\">FAQ</h2>\n",
"\n",
"* **偶奇条件は確認不要か**\n",
" $s^2=4p+d^2$ より $s^2 \\equiv d^2 \\pmod{4}$。$s$ と $d$ の偶奇が一致するため、$x=(s+t)/2,\\ y=(s-t)/2$ は常に整数になる。\n",
"* **符号付きの $s$ を数える理由**\n",
" $s=\\pm q$ はそれぞれ異なる $(x,y)$ を与える可能性があり、有序対の計数では両方を数える必要がある。\n",
"* **なぜ $O(1)$ か**\n",
" 各テストに対して加減乗算と `isqrt`、数回の比較のみで、入力値の大きさに関わらず定数時間で終わるため。\n",
"\n",
"---\n",
"\n",
"**補足(Mermaid と日本語フォント)**: Mermaid 図で日本語が豆腐になる場合、利用側の CSS で `.mermaid { font-family: \"Noto Sans JP\", sans-serif; }` のように指定してください。\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading