diff --git a/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/LongestPalindromicSubstring.ts b/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/LongestPalindromicSubstring.ts new file mode 100644 index 00000000..4b96aead --- /dev/null +++ b/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/LongestPalindromicSubstring.ts @@ -0,0 +1,25 @@ +function longestPalindrome(s: string): string { + if (s.length <= 1) return s; + + let start = 0; + let end = 0; + + const expandAroundCenter = (left: number, right: number): void => { + while (left >= 0 && right < s.length && s[left] === s[right]) { + left--; + right++; + } + // [left+1, right-1] が現在の回文 + if (right - left - 1 > end - start) { + start = left + 1; + end = right - 1; + } + }; + + for (let i = 0; i < s.length; i++) { + expandAroundCenter(i, i); // 奇数長の回文中心 + expandAroundCenter(i, i + 1); // 偶数長の回文中心 + } + + return s.slice(start, end + 1); +} diff --git a/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/LongestPalindromicSubstringDP.ts b/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/LongestPalindromicSubstringDP.ts new file mode 100644 index 00000000..e4920973 --- /dev/null +++ b/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/LongestPalindromicSubstringDP.ts @@ -0,0 +1,37 @@ +function longestPalindromDP(s: string): string { + const n = s.length; + if (n < 2) return s; + + // dp[i][j] = s[i..j] が回文かどうか + const dp: boolean[][] = Array.from({ length: n }, () => Array(n).fill(false)); + + let start = 0; + let maxLen = 1; + + // 1文字は必ず回文 + for (let i = 0; i < n; i++) { + dp[i][i] = true; + } + + // 長さ2以上の部分文字列を検討 + for (let len = 2; len <= n; len++) { + for (let i = 0; i <= n - len; i++) { + const j = i + len - 1; + + if (s[i] === s[j]) { + if (len === 2) { + dp[i][j] = true; + } else { + dp[i][j] = dp[i + 1][j - 1]; + } + + if (dp[i][j] && len > maxLen) { + start = i; + maxLen = len; + } + } + } + } + + return s.substring(start, start + maxLen); +} diff --git a/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/README.md b/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/README.md new file mode 100644 index 00000000..55e938a1 --- /dev/null +++ b/Algorithm/ExpandAroundCenter/leetcode/5. Longest Palindromic Substring/README.md @@ -0,0 +1,168 @@ +以下は、**TypeScript** を使って「最長回文部分文字列」を求める解法です。中心展開法(expand around center)を使って、**O(N²)** の時間計算量で実装できます。 + +--- + +### ✅ コード(TypeScript) + +```ts +function longestPalindrome(s: string): string { + if (s.length <= 1) return s; + + let start = 0; + let end = 0; + + const expandAroundCenter = (left: number, right: number): void => { + while (left >= 0 && right < s.length && s[left] === s[right]) { + left--; + right++; + } + // [left+1, right-1] が現在の回文 + if (right - left - 1 > end - start) { + start = left + 1; + end = right - 1; + } + }; + + for (let i = 0; i < s.length; i++) { + expandAroundCenter(i, i); // 奇数長の回文中心 + expandAroundCenter(i, i + 1); // 偶数長の回文中心 + } + + return s.slice(start, end + 1); +} +``` + +--- + +### 🔍 解説 + +#### 🔸中心展開法とは? + +文字列の各インデックス `i` を中心として、左右に文字が対称かどうかを確認して最長の回文を探す方法です。回文には以下の2パターンがあります: + +* 奇数長(例: `"aba"`) → 中心は1文字 +* 偶数長(例: `"abba"`)→ 中心は2文字 + +#### 🔸処理フロー図(例: `"babad"`) + +``` +i = 0: 中心 = "b" → "bab" が回文 +i = 1: 中心 = "a" → "aba" が回文 +i = 2: 中心 = "b" → "bab" が回文 +i = 3: 中心 = "a" → "a" +... +最長は "bab" または "aba" +``` + +--- + +### 🧪 テスト + +```ts +console.log(longestPalindrome("babad")); // "bab" or "aba" +console.log(longestPalindrome("cbbd")); // "bb" +console.log(longestPalindrome("a")); // "a" +console.log(longestPalindrome("ac")); // "a" or "c" +``` + +--- + +### ⏱️ 計算量 + +* 時間計算量:O(N²) +* 空間計算量:O(1) + +--- + +**動的計画法(DP)を用いた TypeScript 実装** +この方法は、部分文字列 `s[i..j]` が回文かどうかを記録し、そこから最長の回文部分文字列を探索します。 + +--- + +## ✅ TypeScript 実装(動的計画法) + +```ts +function longestPalindrome(s: string): string { + const n = s.length; + if (n < 2) return s; + + // dp[i][j] = s[i..j] が回文かどうか + const dp: boolean[][] = Array.from({ length: n }, () => Array(n).fill(false)); + + let start = 0; + let maxLen = 1; + + // 1文字は必ず回文 + for (let i = 0; i < n; i++) { + dp[i][i] = true; + } + + // 長さ2以上の部分文字列を検討 + for (let len = 2; len <= n; len++) { + for (let i = 0; i <= n - len; i++) { + const j = i + len - 1; + + if (s[i] === s[j]) { + if (len === 2) { + dp[i][j] = true; + } else { + dp[i][j] = dp[i + 1][j - 1]; + } + + if (dp[i][j] && len > maxLen) { + start = i; + maxLen = len; + } + } + } + } + + return s.substring(start, start + maxLen); +} +``` + +--- + +## 🔍 処理概要 + +### 🧠 dp\[i]\[j] の定義: + +* `dp[i][j] = true` なら `s[i..j]` が回文 + +### 🔄 状態遷移: + +* `s[i] !== s[j]` → `dp[i][j] = false` +* `s[i] === s[j]`: + + * 長さ2 → `dp[i][j] = true` + * 長さ3以上 → `dp[i][j] = dp[i+1][j-1]` + +--- + +## 🧪 例: `"babad"` の場合 + +``` +i=0, j=2 -> "bab" => dp[0][2] = dp[1][1] = true → 回文 +i=1, j=3 -> "aba" => dp[1][3] = dp[2][2] = true → 回文 +最長は "bab" または "aba" +``` + +--- + +## ⏱️ 計算量 + +* 時間計算量:`O(N²)` +* 空間計算量:`O(N²)`(2次元配列) + +--- + +## ✅ テスト例 + +```ts +console.log(longestPalindrome("babad")); // "bab" または "aba" +console.log(longestPalindrome("cbbd")); // "bb" +console.log(longestPalindrome("a")); // "a" +console.log(longestPalindrome("ac")); // "a" または "c" +``` + +--- diff --git a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A59/A59.py b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A59/A59.py index 50764d98..b191ebd8 100644 --- a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A59/A59.py +++ b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A59/A59.py @@ -2,34 +2,33 @@ input = sys.stdin.read class FenwickTree: - def __init__(self, size): + def __init__(self, size: int): self.n = size self.tree = [0] * (size + 1) # 1-indexed - def add(self, index, value): + def add(self, index: int, value: int): while index <= self.n: self.tree[index] += value index += index & -index - def sum(self, index): + def sum(self, index: int) -> int: result = 0 while index > 0: result += self.tree[index] index -= index & -index return result - def range_sum(self, left, right): + def range_sum(self, left: int, right: int) -> int: return self.sum(right - 1) - self.sum(left - 1) def main(): data = input().split() N = int(data[0]) - Q = int(data[1]) bit = FenwickTree(N) A = [0] * (N + 1) # 1-indexed - res = [] + res: list[str] = [] # Type hint for the list of strings idx = 2 while idx < len(data): if data[idx] == '1': diff --git a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.go b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.go new file mode 100644 index 00000000..7236577a --- /dev/null +++ b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.go @@ -0,0 +1,94 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" +) + +type FenwickTree struct { + n int + data []int +} + +func NewFenwickTree(n int) *FenwickTree { + return &FenwickTree{ + n: n + 2, + data: make([]int, n+3), + } +} + +func (ft *FenwickTree) Update(i int) { + i++ + for i < ft.n { + ft.data[i]++ + i += i & -i + } +} + +func (ft *FenwickTree) Query(i int) int { + i++ + res := 0 + for i > 0 { + res += ft.data[i] + i -= i & -i + } + return res +} + +func CountInversions(arr []int, N int) int { + ft := NewFenwickTree(N) + inv := 0 + for i := len(arr) - 1; i >= 0; i-- { + inv += ft.Query(arr[i] - 1) + ft.Update(arr[i]) + } + return inv +} + +func main() { + scanner := bufio.NewScanner(os.Stdin) + scanner.Split(bufio.ScanWords) + + nextInt := func() int { + scanner.Scan() + v, _ := strconv.Atoi(scanner.Text()) + return v + } + + N := nextInt() + grid := make([][]int, N) + for i := 0; i < N; i++ { + grid[i] = make([]int, N) + for j := 0; j < N; j++ { + grid[i][j] = nextInt() + } + } + + rowPos := make([]int, N+1) + colPos := make([]int, N+1) + + // 位置記録 + for i := 0; i < N; i++ { + for j := 0; j < N; j++ { + val := grid[i][j] + if val != 0 { + rowPos[val] = i + colPos[val] = j + } + } + } + + rowPerm := make([]int, N) + colPerm := make([]int, N) + for k := 1; k <= N; k++ { + rowPerm[k-1] = rowPos[k] + colPerm[k-1] = colPos[k] + } + + rowMoves := CountInversions(rowPerm, N) + colMoves := CountInversions(colPerm, N) + + fmt.Println(rowMoves + colMoves) +} diff --git a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.js b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.js new file mode 100644 index 00000000..7d366e35 --- /dev/null +++ b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.js @@ -0,0 +1,74 @@ +const fs = require('fs'); + +const input = fs.readFileSync(0, 'utf8').trim().split(/\s+/); +const N = parseInt(input[0]); +const grid = []; +let idx = 1; + +for (let i = 0; i < N; i++) { + const row = []; + for (let j = 0; j < N; j++) { + row.push(Number(input[idx++])); + } + grid.push(row); +} + +// 行と列の現在位置を記録(k=1〜N の位置) +const rowPos = Array(N + 1); +const colPos = Array(N + 1); + +for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + const val = grid[i][j]; + if (val !== 0) { + rowPos[val] = i; + colPos[val] = j; + } + } +} + +// 目的地はそれぞれ (k-1, k-1) に配置すること +const rowPerm = []; // 各kの現在の行インデックス +const colPerm = []; // 各kの現在の列インデックス + +for (let k = 1; k <= N; k++) { + rowPerm.push(rowPos[k]); + colPerm.push(colPos[k]); +} + +// 転倒数を求める関数(BIT/Fenwick Tree使用) +function countInversions(arr) { + const bit = Array(N + 2).fill(0); + let inv = 0; + + function update(i) { + i += 1; + while (i <= N + 1) { + bit[i]++; + i += i & -i; + } + } + + function query(i) { + i += 1; + let res = 0; + while (i > 0) { + res += bit[i]; + i -= i & -i; + } + return res; + } + + // 座標圧縮が不要なのは 0〜N-1 の整数が並ぶため + for (let i = arr.length - 1; i >= 0; i--) { + inv += query(arr[i] - 1); + update(arr[i]); + } + + return inv; +} + +const rowMoves = countInversions(rowPerm); +const colMoves = countInversions(colPerm); + +console.log(rowMoves + colMoves); diff --git a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.php b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.php new file mode 100644 index 00000000..3d309587 --- /dev/null +++ b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.php @@ -0,0 +1,103 @@ +size = $n + 2; + $this->tree = array_fill(0, $this->size + 1, 0); + } + + public function update(int $i): void + { + $i++; + while ($i < $this->size) { + $this->tree[$i]++; + $i += $i & -$i; + } + } + + public function query(int $i): int + { + $i++; + $res = 0; + while ($i > 0) { + $res += $this->tree[$i]; + $i -= $i & -$i; + } + return $res; + } +} + +/** + * 転倒数を数える関数 + * @param int[] $arr + * @param int $N + * @return int + */ +function countInversions(array $arr, int $N): int +{ + $bit = new FenwickTree($N); + $inv = 0; + for ($i = count($arr) - 1; $i >= 0; $i--) { + $inv += $bit->query($arr[$i] - 1); + $bit->update($arr[$i]); + } + return $inv; +} + +// 行・列それぞれの転倒数を計算 +$rowMoves = countInversions($rowPerm, $N); +$colMoves = countInversions($colPerm, $N); + +// 出力 +echo ($rowMoves + $colMoves) . PHP_EOL; \ No newline at end of file diff --git a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.py b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.py new file mode 100644 index 00000000..1a0ac5a1 --- /dev/null +++ b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.py @@ -0,0 +1,56 @@ +import sys + +sys.setrecursionlimit(1000000) +input = sys.stdin.read +data = list(map(int, input().split())) + +N = data[0] +P = [data[i:i+N] for i in range(1, len(data), N)] + +row_pos = [0] * (N + 1) +col_pos = [0] * (N + 1) + +# 各数字kの現在の位置(row, col)を取得 +for i in range(N): + for j in range(N): + val = P[i][j] + if val != 0: + row_pos[val] = i + col_pos[val] = j + +# k=1〜N に対して現在の行・列の順列を作る +row_perm = [row_pos[k] for k in range(1, N + 1)] +col_perm = [col_pos[k] for k in range(1, N + 1)] + +# 転倒数をFenwick Treeで求める +class FenwickTree: + def __init__(self, size: int): + self.N = size + 2 # 安全に余裕を持たせる + self.tree = [0] * self.N + + def update(self, i: int, x: int = 1): + i += 1 + while i < self.N: + self.tree[i] += x + i += i & -i + + def query(self, i: int) -> int: + i += 1 + res = 0 + while i > 0: + res += self.tree[i] + i -= i & -i + return res + +def count_inversions(arr: list[int]) -> int: + ft = FenwickTree(N) + inv = 0 + for i in reversed(arr): + inv += ft.query(i - 1) + ft.update(i) + return inv + +row_moves = count_inversions(row_perm) +col_moves = count_inversions(col_perm) + +print(row_moves + col_moves) diff --git a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.ts b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.ts new file mode 100644 index 00000000..ad71c05d --- /dev/null +++ b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/A74.ts @@ -0,0 +1,73 @@ +import * as fs from 'fs'; + +const input = fs.readFileSync(0, 'utf8').trim().split(/\s+/); +const N = Number(input[0]); +const grid: number[][] = []; + +let idx = 1; +for (let i = 0; i < N; i++) { + const row: number[] = []; + for (let j = 0; j < N; j++) { + row.push(Number(input[idx++])); + } + grid.push(row); +} + +// 数字 k がどの位置 (行, 列) にあるか記録 +const rowPos: number[] = Array(N + 1); +const colPos: number[] = Array(N + 1); + +for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + const val = grid[i][j]; + if (val !== 0) { + rowPos[val] = i; + colPos[val] = j; + } + } +} + +// 各数字の現在位置の順列を構成 +const rowPerm: number[] = []; +const colPerm: number[] = []; + +for (let k = 1; k <= N; k++) { + rowPerm.push(rowPos[k]); + colPerm.push(colPos[k]); +} + +// 転倒数を求める(Fenwick Treeで O(N log N)) +function countInversions(arr: number[]): number { + const BIT: number[] = Array(N + 2).fill(0); + let res = 0; + + const update = (i: number) => { + i++; + while (i <= N + 1) { + BIT[i]++; + i += i & -i; + } + }; + + const query = (i: number): number => { + i++; + let sum = 0; + while (i > 0) { + sum += BIT[i]; + i -= i & -i; + } + return sum; + }; + + for (let i = arr.length - 1; i >= 0; i--) { + res += query(arr[i] - 1); + update(arr[i]); + } + + return res; +} + +const rowMoves = countInversions(rowPerm); +const colMoves = countInversions(colPerm); + +console.log(rowMoves + colMoves); diff --git a/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/README.md b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/README.md new file mode 100644 index 00000000..3f250e90 --- /dev/null +++ b/DataStructures/Trees/BinaryIndexedTree/FenwickTree/atcoder/A74/README.md @@ -0,0 +1,338 @@ + +--- + +## 🧩 問題の概要(再掲) + +与えられた `N × N` の盤面には、1〜Nの整数が1つずつ配置されており、各行・各列に1つだけあります。 +この状態から、**以下の条件を満たす状態にするための最小操作回数**を求めます。 + +> すべての整数 `k` が「**行 `k`(上からk行目)**、**列 `k`(左からk列目)**」にある状態にする。 + +操作として許されるのは: + +* 隣接する **行**の交換 +* 隣接する **列**の交換 + +--- + +## ✅ 入力例 + +```plaintext +N = 4 +盤面: +0 0 2 0 +3 0 0 0 +0 0 0 4 +0 1 0 0 +``` + +### 🟦 図示(元の配置) + +``` + 0 1 2 3 ← 列 +0 [ . . ② . ] +1 [ ③ . . . ] +2 [ . . . ④ ] +3 [ . ① . . ] +↑ 行 +``` + +* ② (2) は (0,2) +* ③ (3) は (1,0) +* ④ (4) は (2,3) +* ① (1) は (3,1) + +--- + +## 🎯 最終的に目指すべき配置 + +「整数 `k` が `(k-1, k-1)` に存在」 + +つまり: + +``` + 0 1 2 3 +0 [ ① . . . ] +1 [ . ② . . ] +2 [ . . ③ . ] +3 [ . . . ④ ] +``` + +--- + +## 🔁 どうやって操作を最小化するか? + +行と列の順番を入れ替えることで、上の目標の形にしたい。 +このとき、以下を考えます: + +* **現在の各数値の行・列番号をリストにする** +* 行の順序・列の順序をそれぞれ目標に合わせるために必要な **隣接スワップの回数(=転倒数)** を求める + +--- + +## 🧮 各数値の現在位置 + +| 数字 k | 行 | 列 | +| ---- | - | - | +| 1 | 3 | 1 | +| 2 | 0 | 2 | +| 3 | 1 | 0 | +| 4 | 2 | 3 | + +それぞれ `(行,列)` の配列にすると: + +```ts +rowPerm = [3, 0, 1, 2] // k=1〜4 の行の現在位置 +colPerm = [1, 2, 0, 3] // k=1〜4 の列の現在位置 +``` + +--- + +## 🔃 目標:rowPerm, colPerm を \[0,1,2,3] にしたい + +この並びを `[0, 1, 2, 3]` にするには「何回隣接要素をスワップすればよいか?」 + +--- + +## 🔢 転倒数(図で説明) + +### `rowPerm = [3, 0, 1, 2]` + +これを図でスワップして整列: + +``` +[3, 0, 1, 2] +→ swap(0,1): [0,3,1,2] (1回) +→ swap(1,2): [0,1,3,2] (2回目) +→ swap(2,3): [0,1,2,3] (3回目) +``` + +✔️ 転倒数 = 3 + +--- + +### `colPerm = [1, 2, 0, 3]` + +``` +[1, 2, 0, 3] +→ swap(1,2): [1,0,2,3] +→ swap(0,1): [0,1,2,3] +→ 計 2回 +``` + +✔️ 転倒数 = 2 + +--- + +## ✅ 結果 + +最小操作回数(=最小スワップ回数) +\= `行の転倒数 3 + 列の転倒数 2 = 5` + +--- + +## 💡 図まとめ + +| ステップ | 配列 | 操作内容 | +| ------- | ---------- | -------------------- | +| rowPerm | \[3,0,1,2] | swap 3↔0 → 0↔1 → 1↔2 | +| colPerm | \[1,2,0,3] | swap 2↔0 → 1↔0 | + +--- + +## 📌 補足:なぜ転倒数でOK? + +隣接スワップだけで順列を並び替えるときの**最小操作回数は転倒数と等しい**からです。 + +* たとえば `[3, 1, 2]` → `[1, 2, 3]` にするには + + * `3 ↔ 1`(1回) + * `3 ↔ 2`(2回) + * 計 2回(= 転倒数) + +--- + +## 🔚 最後に + +この問題は一見難しそうですが、本質は **行・列の並びを目標順列にする** こと。 +そしてそれを**最小スワップ回数(=転倒数)で変換するだけ**です。 + +--- + +この `countInversions` 関数は、**Fenwick Tree(BIT = Binary Indexed Tree)** を使って **転倒数(inversion count)** を効率的に数える方法を実装しています。 +ここでは、次の3点に絞って **図とともに詳しく解説**します。 + +--- + +## 🔍 目次 + +1. **転倒数とは?(定義と例)** +2. **Fenwick Tree(BIT)とは?(図解付き)** +3. **コード解説:`countInversions`の処理ステップ** + +--- + +## ① 転倒数とは? + +配列の中で、**左側にある要素が右側の要素より大きい**とき、それを **転倒(inversion)** と呼びます。 + +### 例 + +```ts +arr = [3, 1, 2] +``` + +* `(3, 1)` → 転倒 +* `(3, 2)` → 転倒 + +➡️ 転倒数 = **2** + +--- + +## ② Fenwick Tree(BIT)とは? + +### ✔ 目的 + +* \*\*区間和(部分和)\*\*を高速に更新・取得できるデータ構造 +* ここでは、\*\*「自分より小さい数が、これまでに何個出てきたか?」\*\*を高速に数える用途で使います。 + +--- + +### ✔ 仕組み(図で解説) + +Fenwick Treeは配列に木構造を埋め込んだような構造で、以下の2操作が O(log N) で行えます。 + +| 操作 | 内容 | +| ----------- | ------------------------------ | +| `update(i)` | 位置 `i` に 1 を加算(=要素iが登場したことを記録) | +| `query(i)` | 位置 `0~i` までの合計(=登場済みの要素数) | + +--- + +### 🧠 使用目的(このコードにおいて) + +* 配列を後ろから見ていき、 + + * 「**今見ている要素 `arr[i]` より小さい数(= すでに出現済みの要素)の個数**」を `query(arr[i] - 1)` で取得 + * それを転倒数に加算 +* そして、自分自身 `arr[i]` を `update(arr[i])` で記録しておく + +--- + +## ③ コードステップでの解説 + +```ts +function countInversions(arr: number[]): number { + const BIT: number[] = Array(N + 2).fill(0); + let res = 0; +``` + +* `BIT` = Fenwick Tree。サイズは N+2(安全のため+2) +* `res` = 転倒数を格納する変数 + +--- + +### `update(i)` + +```ts + const update = (i: number) => { + i++; + while (i <= N + 1) { + BIT[i]++; + i += i & -i; + } + }; +``` + +* BITのインデックスは1-basedにするため `i++`。 +* `BIT[i]++` でカウント増やす。 +* `i += i & -i` は「親ノード」へ移動。 + +--- + +### `query(i)` + +```ts + const query = (i: number): number => { + i++; + let sum = 0; + while (i > 0) { + sum += BIT[i]; + i -= i & -i; + } + return sum; + }; +``` + +* 1〜iまでの合計を求める。 +* `i -= i & -i` は「親ノードに遡る」操作。 + +--- + +### ループ処理本体 + +```ts + for (let i = arr.length - 1; i >= 0; i--) { + res += query(arr[i] - 1); // 自分より小さい要素の数 + update(arr[i]); // 自分を記録 + } + + return res; +``` + +* 後ろから見ることで、「右にある値と比較する転倒」のカウントになる。 +* `query(arr[i] - 1)` で「自分より小さい値がどれだけすでに出てきたか」を取得。 +* `update(arr[i])` で自分の値をBITに登録。 + +--- + +## 🧪 例で確認 + +```ts +arr = [3, 1, 2] +処理順:右→左(i = 2 → 1 → 0) + +i = 2 → arr[2] = 2 + query(1) = 0 ⇒ 転倒なし + update(2) + +i = 1 → arr[1] = 1 + query(0) = 0 ⇒ 転倒なし + update(1) + +i = 0 → arr[0] = 3 + query(2) = 2 ⇒ 2つ転倒(1,2) + update(3) + +最終 res = 2 ✅ +``` + +--- + +## 📊 計算量 + +| 処理 | 時間計算量 | +| ------------------------ | ---------- | +| `update(i)` / `query(i)` | O(log N) | +| 全体 | O(N log N) | + +--- + +## 📌 まとめ + +| 項目 | 内容 | +| ---- | --------------------------- | +| 目的 | 隣接スワップで整列させる最小操作回数(転倒数)を求める | +| 使用構造 | Fenwick Tree(BIT) | +| 処理内容 | 「小さい数が右にどれだけいるか」を数える | +| 方向 | 右から左へスキャンして更新 | + +--- + +| [提出日時](https://atcoder.jp/contests/tessoku-book/submissions/me?desc=true&orderBy=created) | 問題 | ユーザ | 言語 | [得点](https://atcoder.jp/contests/tessoku-book/submissions/me?desc=true&orderBy=score) | [コード長](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=source_length) | 結果 | [実行時間](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=time_consumption) | [メモリ](https://atcoder.jp/contests/tessoku-book/submissions/me?orderBy=memory_consumption) | | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 2025-06-29 17:07:27 | [A74 - Board Game](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bv) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [PHP (php 8.2.8)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5016) | 1000 | 2067 Byte | **AC** | 17 ms | 22300 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67181473) | +| 2025-06-29 17:04:25 | [A74 - Board Game](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bv) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [Go (go 1.20.6)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5002) | 1000 | 1532 Byte | **AC** | 1 ms | 1672 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67181426) | +| 2025-06-29 17:02:26 | [A74 - Board Game](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bv) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [Python (CPython 3.11.4)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5055) | 1000 | 1400 Byte | **AC** | 13 ms | 9252 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67181382) | +| 2025-06-29 16:42:49 | [A74 - Board Game](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bv) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [TypeScript 5.1 (Node.js 18.16.1)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5058) | 1000 | 1564 Byte | **AC** | 41 ms | 43848 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67181017) | +| 2025-06-29 16:38:32 | [A74 - Board Game](https://atcoder.jp/contests/tessoku-book/tasks/tessoku_book_bv) | [myoshizumi](https://atcoder.jp/users/myoshizumi) | [JavaScript (Node.js 18.16.1)](https://atcoder.jp/contests/tessoku-book/submissions/me?f.Language=5009) | 1000 | 1621 Byte | **AC** | 44 ms | 43684 KiB | [詳細](https://atcoder.jp/contests/tessoku-book/submissions/67180939) | \ No newline at end of file