From 4905fe669549f8658447cec66aeba4072198cfca Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Tue, 8 Jul 2025 21:22:00 +0900 Subject: [PATCH 1/2] =?UTF-8?q?atcoder=20B13=20-=20Supermarket=202=20?= =?UTF-8?q?=E3=81=97=E3=82=83=E3=81=8F=E3=81=A8=E3=82=8A=E6=B3=95=20(Two?= =?UTF-8?q?=20Pointers)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ASCIICodeExpansion.js | 127 ++++++++++++ .../ASCIICodeExpansion.py | 46 ++++ .../ASCII code expansion/README.md | 196 ++++++++++++++++++ Algorithm/TwoPointers/atcoder/B13/B13.go | 126 +++++++++++ Algorithm/TwoPointers/atcoder/B13/B13.js | 64 ++++++ Algorithm/TwoPointers/atcoder/B13/B13.php | 96 +++++++++ Algorithm/TwoPointers/atcoder/B13/B13.py | 83 ++++++++ Algorithm/TwoPointers/atcoder/B13/B13.ts | 72 +++++++ Algorithm/TwoPointers/atcoder/B13/README.md | 186 +++++++++++++++++ 9 files changed, 996 insertions(+) create mode 100644 Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.js create mode 100644 Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.py create mode 100644 Algorithm/RSA encryption/ASCII code expansion/README.md create mode 100644 Algorithm/TwoPointers/atcoder/B13/B13.go create mode 100644 Algorithm/TwoPointers/atcoder/B13/B13.js create mode 100644 Algorithm/TwoPointers/atcoder/B13/B13.php create mode 100644 Algorithm/TwoPointers/atcoder/B13/B13.py create mode 100644 Algorithm/TwoPointers/atcoder/B13/B13.ts create mode 100644 Algorithm/TwoPointers/atcoder/B13/README.md diff --git a/Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.js b/Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.js new file mode 100644 index 00000000..7cd8e9c5 --- /dev/null +++ b/Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.js @@ -0,0 +1,127 @@ +// 以下は、**RSA復号処理 + ASCIIコード展開(28ビット → 7ビット×4分割)** を行うJavaScriptコードです。 +// `fs` モジュールで標準入力から値を取得し、**実行時間・メモリの消費を抑えた実装**になっています。 + +// --- + +// ## ✅ 処理概要 + +// 1. 入力:`n`, `e`, `E` +// 2. φ(n) を求めるために、`n` を素因数分解 → `p`, `q` を得る +// 3. `φ(n) = (p-1)(q-1)` を計算 +// 4. `d = e⁻¹ mod φ(n)` を求める(拡張ユークリッド) +// 5. `M = E^d mod n`(繰り返し二乗法) +// 6. `M` を28ビットで見て、7ビットごとに区切って文字列化 + +// --- + +// ## ✅ コード(Node.js) + +const fs = require('fs'); + +// 入力取得 +const input = fs.readFileSync(0, 'utf8').trim(); +const [nRaw, eRaw, Eraw] = input.split(' ').map(BigInt); + +const n = nRaw; +const e = eRaw; +const E = Eraw; + +// 素因数分解(p, q) +// n は 10^10 以下なので √n まで試せば良い +function findFactors(n) { + const sqrtN = BigInt(Math.floor(Math.sqrt(Number(n)))); + for (let i = 2n; i <= sqrtN; i++) { + if (n % i === 0n) { + return [i, n / i]; + } + } + return [1n, n]; // fallback +} + +// 拡張ユークリッド互除法で逆元を求める +function modInv(a, m) { + let m0 = m, x0 = 0n, x1 = 1n; + while (a > 1n) { + const q = a / m; + [a, m] = [m, a % m]; + [x0, x1] = [x1 - q * x0, x0]; + } + return x1 < 0n ? x1 + m0 : x1; +} + +// 繰り返し二乗法で a^b % m を高速計算 +function modPow(base, exp, mod) { + let result = 1n; + base = base % mod; + while (exp > 0n) { + if (exp % 2n === 1n) { + result = (result * base) % mod; + } + base = (base * base) % mod; + exp = exp / 2n; + } + return result; +} + +// 1. 素因数分解 +const [p, q] = findFactors(n); + +// 2. φ(n) = (p-1)(q-1) +const phi = (p - 1n) * (q - 1n); + +// 3. 秘密鍵 d = e^-1 mod φ(n) +const d = modInv(e, phi); + +// 4. 復号処理 M = E^d mod n +const M = modPow(E, d, n); + +// 5. 28ビットを 7ビット × 4 に分割し ASCII 変換 +const chars = []; +let temp = M; +for (let i = 0; i < 4; i++) { + const charCode = Number(temp & 0b1111111n); // 下位7ビット + if (charCode !== 0) { + chars.unshift(String.fromCharCode(charCode)); + } + temp >>= 7n; +} + +// 出力 +console.log(chars.join('')); + +// ## ✅ 実行例 + +// ### 入力: + +// ``` +// 3995747143 3007 602607029 +// ``` + +// ### 出力: + +// ``` +// PAIZ +// ``` + +// --- + +// ## ✅ 実行時間・メモリ分析 + +// | ステップ | 時間計算量 | 備考 | +// | --------------- | ----------- | ------------------------- | +// | 素因数分解 | O(√n) | n ≤ 10^10 なので最大 10^5 回ループ | +// | 逆元計算(modInv) | O(log φ(n)) | log(10^10) ≈ 33 回以下 | +// | 繰り返し二乗法(modPow) | O(log d) | 非常に高速 | +// | ビット操作&変換 | O(1) | 最大4文字分 | +// | **総メモリ使用量** | 数百 KB 程度 | BigInt 計算のために多少のオーバーヘッドあり | + +// --- + +// ## ✅ 補足 + +// * 暗号文(`E`)が 28 ビットの復号文 `M` を生成する前提です。 +// * `M` は ASCII コードの7ビット(0〜127)範囲の文字しか使わない設計です。 + +// --- + +// ご希望があれば、RSAの暗号化側(E = M^e mod n)コードや、多文字列対応の拡張版なども提供可能です。お気軽にどうぞ! diff --git a/Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.py b/Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.py new file mode 100644 index 00000000..66dc944d --- /dev/null +++ b/Algorithm/RSA encryption/ASCII code expansion/ASCIICodeExpansion.py @@ -0,0 +1,46 @@ +from typing import Tuple + +def extgcd(a: int, b: int) -> Tuple[int, int, int]: + if b != 0: + c, y, x = extgcd(b, a % b) + y -= (a // b) * x + return c, x, y + return a, 1, 0 + +def modpow(a: int, b: int, m: int) -> int: + ans = 1 + while 0 < b: + if b & 1 == 1: + ans = (ans * a) % m + a = (a * a) % m + b >>= 1 + return ans + +def divisor(n: int) -> Tuple[int, int]: + for i in range(2, int(n ** (1 / 2))): + if n % i == 0: + p = i + q = n // i + return p, q + raise ValueError("No divisors found") + +n, e, E = map(int, input().split()) +p, q = divisor(n) +n_prime = (p - 1) * (q - 1) + +c, x, y = extgcd(e, n_prime) +d = (x + n_prime) % n_prime +message = modpow(E, d, n) # Renamed from M to message + +letter = [0] * 4 +for i in range(4): + for j in range(7): + if message % 2 == 1: + letter[i] += pow(2, j) + message //= 2 + +output = "" +for i in range(4): + if letter[3 - i] != 0: + output += chr(letter[3 - i]) +print(output) \ No newline at end of file diff --git a/Algorithm/RSA encryption/ASCII code expansion/README.md b/Algorithm/RSA encryption/ASCII code expansion/README.md new file mode 100644 index 00000000..dbc7f313 --- /dev/null +++ b/Algorithm/RSA encryption/ASCII code expansion/README.md @@ -0,0 +1,196 @@ + +--- + +## 🔐 問題の背景と目的 + +RSA暗号では以下の数式に基づいて **秘密の数字 M** を安全にやりとりできます。 + +* 暗号化:`E ≡ M^e mod n` +* 復号化:`M ≡ E^d mod n` + +さらに今回は、`M`(28ビット)を **7ビットごとに区切ってASCII文字に復元**します。 + +--- + +## 🧩 入力例 + +``` +n = 3995747143 +e = 3007 +E = 602607029 +``` + +--- + +## 🪜 処理ステップ図解(全体) + +```mermaid +flowchart TD + A[入力: n, e, E] --> B[素因数分解 n = p × q] + B --> C[φ(n) = (p-1)(q-1)] + C --> D[d = e⁻¹ mod φ(n)] + D --> E[M = E^d mod n] + E --> F[28ビット → 7ビット×4 に分割] + F --> G[ASCIIコード → 文字列] +``` + +--- + +## 🔹 ステップ①:n の素因数分解(p, q) + +``` +n = 3995747143 = p × q +``` + +調べると、 + +``` +p = 59359 +q = 67309 +``` + +#### 図: + +``` ++-----------------------------+ +| 素因数分解 | ++-----------------------------+ +| n = pq | +| p = 59359 , q = 67309 | ++-----------------------------+ +``` + +--- + +## 🔹 ステップ②:φ(n) の計算 + +``` +φ(n) = (p - 1) × (q - 1) + = 59358 × 67308 = 3995620776 +``` + +#### 図: + +``` ++--------------------------------+ +| オイラー関数 φ(n) | ++--------------------------------+ +| φ(n) = (p - 1)(q - 1) | +| = 59358 × 67308 | +| = 3995620776 | ++--------------------------------+ +``` + +--- + +## 🔹 ステップ③:秘密鍵 d の計算 + +秘密鍵 `d` は以下を満たす逆元: + +``` +d ≡ e⁻¹ mod φ(n) +``` + +`e = 3007`、`φ(n) = 3995620776` に対して、 + +``` +d = 1899220895 +``` + +これは拡張ユークリッド互除法で求めます。 + +#### 図: + +``` ++---------------------------------------------------+ +| 秘密鍵 d の計算(逆元) | ++---------------------------------------------------+ +| e × d ≡ 1 mod φ(n) | +| → 3007 × d ≡ 1 mod 3995620776 | +| → d = 1899220895 | ++---------------------------------------------------+ +``` + +--- + +## 🔹 ステップ④:復号 `M = E^d mod n` + +与えられた `E = 602607029` を復号します: + +``` +M = E^d mod n + = 602607029^1899220895 mod 3995747143 + = 1347701416 +``` + +(繰り返し二乗法を使って効率的に計算) + +#### 図: + +``` ++-----------------------------------------+ +| 復号処理:M = E^d mod n | ++-----------------------------------------+ +| E = 602607029 | +| d = 1899220895 | +| n = 3995747143 | +| → M = 1347701416 | ++-----------------------------------------+ +``` + +--- + +## 🔹 ステップ⑤:28ビット → 7ビット × 4 分割 + +28ビット表現の `M = 1347701416` は 2進数で: + +``` +M = 01010000 01000001 01001001 01011010 + P A I Z +``` + +上位から7ビットずつ分割: + +| S\[0] | S\[1] | S\[2] | S\[3] | +| ----- | ----- | ----- | ----- | +| 80 | 65 | 73 | 90 | +| 'P' | 'A' | 'I' | 'Z' | + +#### 図: + +``` ++-----------------------------------------------------+ +| 28ビット分解: 1347701416 | ++-----------------------------------------------------+ +| 2進数:01010000 01000001 01001001 01011010 | +| ^ ^ ^ ^ | +| S[0] S[1] S[2] S[3] | +| 80 65 73 90 | +| 'P' 'A' 'I' 'Z' | ++-----------------------------------------------------+ +``` + +--- + +## ✅ 最終出力 + +```txt +PAIZ +``` + +--- + +## 🧠 処理全体まとめ(再掲) + +```mermaid +graph LR + A1[n, e, E 入力] --> A2[素因数分解 → p, q] + A2 --> A3[φ(n) 計算] + A3 --> A4[秘密鍵 d 計算(mod逆元)] + A4 --> A5[復号 M = E^d mod n] + A5 --> A6[28ビットを 7ビット × 4 分割] + A6 --> A7[ASCIIコード → 文字列] + A7 --> A8[出力:PAIZ] +``` + +--- diff --git a/Algorithm/TwoPointers/atcoder/B13/B13.go b/Algorithm/TwoPointers/atcoder/B13/B13.go new file mode 100644 index 00000000..090cf5df --- /dev/null +++ b/Algorithm/TwoPointers/atcoder/B13/B13.go @@ -0,0 +1,126 @@ +// 以下に、**Go (v1.20.6)** を用いた、しゃくとり法(二重ポインタ)による高速解法を提示します。 +// 時間・メモリ効率を意識しており、**`O(N)` 時間、`O(N)` メモリ**で動作します。 + +// --- + +// ## ✅ 問題再掲(要約) + +// * 配列 `A[0...N-1]` の連続部分列のうち、**和が K 以下**の個数を数える。 +// * 制約:`1 ≤ N ≤ 10^5`, `1 ≤ K, A[i] ≤ 10^9` +// * 実行時間制限:1 秒、メモリ制限:1024 MiB + +// --- + +// ## ✅ Go 実装(型明示・高速処理) + +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func main() { + // 高速読み取りのため bufio.Scanner 使用 + reader := bufio.NewReader(os.Stdin) + + // 1行目:N, K + line1, _ := reader.ReadString('\n') + parts := strings.Fields(line1) + N, _ := strconv.Atoi(parts[0]) + K, _ := strconv.Atoi(parts[1]) + + // 2行目:A[0..N-1] + line2, _ := reader.ReadString('\n') + aParts := strings.Fields(line2) + A := make([]int, N) + for i := 0; i < N; i++ { + A[i], _ = strconv.Atoi(aParts[i]) + } + + // Two pointers(しゃくとり法) + var count int = 0 + var sum int = 0 + var right int = 0 + + for left := 0; left < N; left++ { + for right < N && sum+A[right] <= K { + sum += A[right] + right++ + } + + count += right - left + + if right == left { + right++ + } else { + sum -= A[left] + } + } + + fmt.Println(count) +} + +// ## ✅ 処理内容と型の明示 + +// | 変数名 | 型 | 説明 | +// | ------------------------------- | ------- | ------------ | +// | `N`, `K` | `int` | 入力サイズ・上限 | +// | `A` | `[]int` | 配列(最大10⁵要素) | +// | `sum`, `count`, `left`, `right` | `int` | 計算用ポインタ・累積変数 | + +// --- + +// ## ✅ パフォーマンスとリソース + +// | 項目 | 評価 | +// | ----- | ---------------------------- | +// | 時間計算量 | `O(N)` | +// | 空間計算量 | `O(N)`(配列Aのみ) | +// | メモリ目安 | ≒ 400KB(`10⁵ × 4B`) | +// | 実行速度 | 数十ms(`go run` or `go build`) | + +// --- + +// ## ✅ 入力例 + +// ```text +// 7 50 +// 11 12 16 22 27 28 31 +// ``` + +// ```bash +// go run main.go < input.txt +// ``` + +// 出力: + +// ``` +// 13 +// ``` + +// --- + +// ## ✅ 処理フロー概要(図解) + +// ``` +// [11, 12, 16, 22, 27, 28, 31] +// ↑ left +// ↑ right +// sum = 0 → 右へ進めて、合計 ≤ K の最大範囲 → count += (right - left) +// → left を進めて同様に処理 +// ``` + +// --- + +// ## ✅ 補足:高速 I/O の工夫 + +// * `bufio.NewReader` で1行ずつ高速読み込み +// * `strings.Fields()` により空白文字を自動分割(タブ・空白など) +// * すべての `Atoi` は `int` 型で扱い、64bit対応不要(Go の `int` は 64bit) + + +// 複数行に渡る A\[i] 入力形式や、ベンチマーク用コードの追加、`bufio.Scanner` 使用への切り替えも可能です。 diff --git a/Algorithm/TwoPointers/atcoder/B13/B13.js b/Algorithm/TwoPointers/atcoder/B13/B13.js new file mode 100644 index 00000000..16ab5bda --- /dev/null +++ b/Algorithm/TwoPointers/atcoder/B13/B13.js @@ -0,0 +1,64 @@ +// この問題は **連続部分列の和が K 以下となる個数** を高速に数える問題です。制約が厳しいため、ナイーブな `O(N^2)` 解法では間に合いません。 + +// ### 解法概要(Two Pointers 法): + +// * 連続部分列なので、\*\*しゃくとり法(二重ポインタ)\*\*を使います。 +// * `left` から始めて、`right` を右へ進めながら部分和を計算し、合計が `K` 以下である範囲の個数をカウント。 + +// ### 計算量: + +// * 時間計算量:`O(N)`(各要素につき `right` が一度しか進まない) +// * 空間計算量:`O(1)`(配列と変数のみ) + +// ### ✅ 実装(`fs` 使用, JavaScript): + +const fs = require("fs"); + +// 入力読み込み +const input = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n"); +const [N, K] = input[0].split(" ").map(Number); +const A = input[1].split(" ").map(Number); + +// Two pointers 法(しゃくとり法) +let count = 0; +let sum = 0; +let right = 0; + +for (let left = 0; left < N; left++) { + while (right < N && sum + A[right] <= K) { + sum += A[right]; + right++; + } + count += right - left; + + if (right === left) { + right++; + } else { + sum -= A[left]; + } +} + +console.log(count); + +// ### 🔍 入力例での処理フロー(簡略図解): + +// 入力: +// `7 50` +// `11 12 16 22 27 28 31` + +// * `(0, 0)`: 11 ✅ → `sum = 11` +// * `(0, 1)`: 11+12 = 23 ✅ +// * `(0, 2)`: 11+12+16 = 39 ✅ +// * `(0, 3)`: 61 ❌ → right止まって、(0,3) 以上は NG +// → → → 次は left = 1 → 以降同様 + +// 合計で13通り。 + +// --- + +// ### ✅ 処理時間・メモリ使用の工夫ポイント: + +// * **一度に全要素読み取り (`fs.readFileSync`)** +// * **不要な再計算を避ける (`sum`の再利用)** +// * **定数倍も意識し、`split(" ")`最小限使用** + diff --git a/Algorithm/TwoPointers/atcoder/B13/B13.php b/Algorithm/TwoPointers/atcoder/B13/B13.php new file mode 100644 index 00000000..2729a66e --- /dev/null +++ b/Algorithm/TwoPointers/atcoder/B13/B13.php @@ -0,0 +1,96 @@ +