From f603236a4805aae0a18661ab9ea2c8ff033dd8a9 Mon Sep 17 00:00:00 2001 From: Wong Ding Feng Date: Tue, 26 May 2026 01:30:51 +0800 Subject: [PATCH] Add remaining flashcards and reference files (qn_01, segment_tree, ds, learning) --- .learning/config.json | 5 + .../cpp-pointers-and-references/mastery.md | 20 ++ .../cpp-pointers-and-references/metadata.json | 9 + .../cpp-pointers-and-references/progress.md | 5 + .../review_schedule.json | 3 + .../cpp-pointers-and-references/syllabus.md | 86 ++++++ org/ds/bit_tree.org | 77 +++++ org/questions/qn_01.org | 268 ++++++++++++++++++ org/questions/subarray-patterns.org | 28 +- org/segment_tree.org | 152 ++++++++++ 10 files changed, 639 insertions(+), 14 deletions(-) create mode 100644 .learning/config.json create mode 100644 .learning/cpp-pointers-and-references/mastery.md create mode 100644 .learning/cpp-pointers-and-references/metadata.json create mode 100644 .learning/cpp-pointers-and-references/progress.md create mode 100644 .learning/cpp-pointers-and-references/review_schedule.json create mode 100644 .learning/cpp-pointers-and-references/syllabus.md create mode 100644 org/ds/bit_tree.org create mode 100644 org/questions/qn_01.org create mode 100644 org/segment_tree.org diff --git a/.learning/config.json b/.learning/config.json new file mode 100644 index 0000000..71c25ff --- /dev/null +++ b/.learning/config.json @@ -0,0 +1,5 @@ +{ + "initialized": true, + "learning_mode": "balanced", + "macos_reminders_enabled": false +} diff --git a/.learning/cpp-pointers-and-references/mastery.md b/.learning/cpp-pointers-and-references/mastery.md new file mode 100644 index 0000000..9e157da --- /dev/null +++ b/.learning/cpp-pointers-and-references/mastery.md @@ -0,0 +1,20 @@ +# C++ Pointers and References - Mastery Checklist + +## Core Concepts + +Track your understanding of key concepts: + +- [ ] Declaration syntax: & and * placement +- [ ] Pointers vs references: mental model +- [ ] Pointer arithmetic +- [ ] const correctness with pointers and references +- [ ] References as function parameters +- [ ] Pointer-to-pointer +- [ ] Null pointers and nullptr +- [ ] Dangling pointers and references +- [ ] Smart pointers (unique_ptr, shared_ptr, weak_ptr) +- [ ] Lvalue references vs rvalue references +- [ ] Move semantics +- [ ] Perfect forwarding +- [ ] Reference collapsing rules +- [ ] Common interview traps diff --git a/.learning/cpp-pointers-and-references/metadata.json b/.learning/cpp-pointers-and-references/metadata.json new file mode 100644 index 0000000..ef005fc --- /dev/null +++ b/.learning/cpp-pointers-and-references/metadata.json @@ -0,0 +1,9 @@ +{ + "topic": "C++ Pointers and References", + "created_at": "2026-05-20T12:00:00", + "status": "in_progress", + "syllabus_generated": true, + "total_sessions": 0, + "last_session": null, + "last_reviewed": null +} diff --git a/.learning/cpp-pointers-and-references/progress.md b/.learning/cpp-pointers-and-references/progress.md new file mode 100644 index 0000000..243ad4b --- /dev/null +++ b/.learning/cpp-pointers-and-references/progress.md @@ -0,0 +1,5 @@ +# C++ Pointers and References - Learning Progress + +## Daily Logs + + diff --git a/.learning/cpp-pointers-and-references/review_schedule.json b/.learning/cpp-pointers-and-references/review_schedule.json new file mode 100644 index 0000000..dd8e3b3 --- /dev/null +++ b/.learning/cpp-pointers-and-references/review_schedule.json @@ -0,0 +1,3 @@ +{ + "reviews": [] +} diff --git a/.learning/cpp-pointers-and-references/syllabus.md b/.learning/cpp-pointers-and-references/syllabus.md new file mode 100644 index 0000000..41ed8e1 --- /dev/null +++ b/.learning/cpp-pointers-and-references/syllabus.md @@ -0,0 +1,86 @@ +# C++ Pointers and References — Syllabus + +## Overview + +Master C++ indirection mechanisms from declaration syntax through move semantics. Emphasis on building correct mental models, real-world usage patterns, and surviving interview questions about pointers and references. + +## Prerequisites + +- Basic C++ syntax (variables, functions, structs/classes) +- Understanding of stack vs heap (conceptual) + +## Learning Objectives + +By completion you will be able to: +- Read and write any pointer/reference declaration confidently +- Explain the difference between `&`/`*` in declarations vs expressions +- Use const-correctness with pointers and references correctly +- Understand ownership semantics and choose the right smart pointer +- Handle move semantics and forwarding references +- Answer common interview questions on pointers and references + +--- + +## Phase 1: Foundations — Declaration Syntax & Mental Models + +- [ ] `&` and `*` in declarations vs expressions — the two contexts +- [ ] Does `*` / `&` bind to the type or the variable? (C++ parsing rules) +- [ ] Multiple declarations on one line: `int *a, b` — what is b? +- [ ] Pointers: what they are (address), what they hold, how to dereference +- [ ] References: what they are (alias), how they differ from pointers +- [ ] Pointer vs reference: when to use which (guidelines) +- [ ] Null pointers: NULL vs nullptr vs 0 + +**Teaching Milestone:** Explain the "declaration follows use" principle to a beginner. + +## Phase 2: const Correctness & Function Signatures + +- [ ] `const int*` vs `int* const` vs `const int* const` +- [ ] `const int&` — why it's the default parameter style +- [ ] Top-level const vs low-level const +- [ ] East const vs West const style +- [ ] Passing by value vs by reference vs by pointer — tradeoffs +- [ ] Return by reference: when safe, when dangerous +- [ ] Pointer arithmetic basics + +**Teaching Milestone:** Read a complex function signature and explain what each parameter accepts and whether it can be modified. + +## Phase 3: Ownership & Smart Pointers + +- [ ] Raw pointer ownership problems (leaks, double free, dangling) +- [ ] RAII principle +- [ ] `std::unique_ptr` — exclusive ownership +- [ ] `std::shared_ptr` — shared ownership and reference counting +- [ ] `std::weak_ptr` — breaking cycles +- [ ] Choosing the right smart pointer (decision tree) +- [ ] `std::make_unique` / `std::make_shared` — why prefer them + +**Teaching Milestone:** Refactor a raw-pointer design to use smart pointers and explain your choices. + +## Phase 4: Move Semantics & Advanced References + +- [ ] Lvalue vs rvalue — what are they really +- [ ] Lvalue references (`T&`) vs rvalue references (`T&&`) +- [ ] `std::move` — what it does and doesn't do +- [ ] Move constructors and move assignment +- [ ] Reference collapsing rules +- [ ] Forwarding references (`T&&` in templates) +- [ ] `std::forward` — perfect forwarding +- [ ] Common interview traps and trick questions + +**Teaching Milestone:** Explain why `std::move` doesn't actually move anything. + +--- + +## Resources + +- cppreference.com — authoritative reference +- "Effective Modern C++" by Scott Meyers — Items 1-6, 23-26 +- Herb Sutter's GotW articles on const correctness + +## Success Criteria + +- Can read any declaration with `*`, `&`, `const` and explain it +- Can choose between pointer, reference, and smart pointer for any use case +- Can explain move semantics without hand-waving +- Can spot pointer/reference bugs in code review diff --git a/org/ds/bit_tree.org b/org/ds/bit_tree.org new file mode 100644 index 0000000..5fe0baf --- /dev/null +++ b/org/ds/bit_tree.org @@ -0,0 +1,77 @@ +#+title: Bit Tree +* binary indexd tree +#+begin_src python :results output +class BinaryIndexedTree: + def __init__(self, n: int): + self.tree = [0] * (n + 1) + def update(self, i: int, delta: int) -> None: + """Add delta to element at index i (0-based).""" + idx = i + 1 + while idx < len(self.tree): + self.tree[idx] += delta + idx += idx & (-idx) + def query(self, i: int) -> int: + """Return prefix sum from 0 to i (0-based).""" + idx = i + 1 + s = 0 + while idx > 0: + s += self.tree[idx] + idx -= idx & (-idx) + return s + def range_query(self, l: int, r: int) -> int: + """Return sum from l to r (0-based, inclusive).""" + if l == 0: + return self.query(r) + return self.query(r) - self.query(l - 1) +#+end_src +- update(i, delta) — adds delta to index i in O(log n) +- query(i) — prefix sum [0..i] in O(log n) +- range_query(l, r) — sum [l..r] in O(log n) + +* min bit tree +That's a min-Fenwick (prefix minimum). Unlike sum-Fenwick, updates are point-set (not add-delta), and increasing a value is expensive — you'd need a segment tree for that. +#+begin_src python +import math +class MinFenwick: + def __init__(self, n: int, inf: int = math.inf): + self.n = n + self.inf = inf + self.tree = [inf] * (n + 1) + self.arr = [inf] * n # track actual values for rebuild + def update(self, i: int, val: int) -> None: + """Set element at index i to val (0-based).""" + self.arr[i] = val + idx = i + 1 + while idx <= self.n: + # rebuild this node from its covered range + lo = idx - (idx & (-idx)) # 0-based: lo + hi = idx - 1 # 0-based: hi + self.tree[idx] = min(self.arr[j] for j in range(lo, hi + 1)) + idx += idx & (-idx) + def query(self, i: int) -> int: + """Return min from 0 to i (0-based, inclusive).""" + idx = i + 1 + res = self.inf + while idx > 0: + res = min(res, self.tree[idx]) + idx -= idx & (-idx) + return res +#+end_src +- update(i, val) — set index i to val, O(log n · span) where span is the size of the covered range +- query(i) — prefix min [0..i], O(log n) +Caveat: If you need to increase a value (removing a minimum from consideration), this rebuilds the range each time — worst case O(n). For full range-min with arbitrary increases/decreases, use a segment tree instead. + +* why doesn't min work with BIT? +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Why doesn't min work well with Fenwick/Bit trees? What property does addition have that min lacks? +** Back +#+begin_quote +Fenwick trees require an =invertible= operation (a group). Addition is invertible via subtraction: range_query(l,r) = prefix(r) - prefix(l-1). Min has no inverse — you can't "subtract out" the minimum of [0..l-1] from prefix(r) to get [l..r]. + +Additionally, min's =update= breaks on value increases. Decreasing a value works fine (like sum's decrease), but increasing a value means the old minimum might have been removed, and you'd need to scan all elements in the node's covered range to find the new minimum. This makes updates O(span) instead of O(log n). + +Good Fenwick operations are associative, have an identity element, and are incrementally updateable: sum, xor, gcd. Min/max work for prefix queries only, not range queries. +#+end_quote diff --git a/org/questions/qn_01.org b/org/questions/qn_01.org new file mode 100644 index 0000000..3019eba --- /dev/null +++ b/org/questions/qn_01.org @@ -0,0 +1,268 @@ +#+title: Master Subarray Pattern Sheet +* Subarray Divisible by K — Remainder Pattern [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Why does "subarray sum divisible by K" work with prefix remainders? + +** Back +If Sum(i..j) mod K = 0, then: + (Prefix[j] - Prefix[i-1]) mod K = 0 + +By modular arithmetic: + Prefix[j] mod K = Prefix[i-1] mod K + +So we find pairs of indices with the same prefix remainder. + +Transformation: store current_sum % K in hash map. + +Data structure: hash map tracking frequencies of remainders. + +C++ caveat: % can return negative for negative operands. Fix: + remainder = ((prefix_sum % K) + K) % K + +Python: % always non-negative, no fix needed. + +* Subarray Equal 0s and 1s — Value Mapping [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Find the longest subarray with equal number of 0s and 1s. +Example: nums = [0,1] → Output: 2 + +** Back +Replace all 0s with -1. The problem becomes: +**"Find a subarray whose sum equals 0."** + +#+begin_src c++ +int findMaxLength(vector& nums) { + unordered_map firstOccurrence; + firstOccurrence[0] = 0; + int sum = 0, maxLen = 0; + for (int i = 0; i < nums.size(); i++) { + sum += (nums[i] == 1) ? 1 : -1; + if (firstOccurrence.count(sum)) { + maxLen = max(maxLen, i + 1 - firstOccurrence[sum]); + } else { + firstOccurrence[sum] = i + 1; + } + } + return maxLen; +} +#+end_src +Time: O(n), Space: O(n) + +Data structure: hash map tracking raw prefix sums (first occurrence). + +* Subarray Equal Odd/Even Numbers — Same Pattern [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Find the longest subarray with equal number of odd and even numbers. + +** Back +Map every even number to +1 and every odd number to -1. +The problem becomes: **"Find a subarray whose sum equals 0."** + +Same approach as equal 0s and 1s: prefix sum + hash map storing first occurrence. + +This is the same value mapping pattern applied to a different domain. + +* Subarray Equal Vowels and Consonants [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Find the longest subarray with equal number of vowels and consonants. + +** Back +Map: vowel → +1, consonant → -1. +The problem becomes: **"Find a subarray whose sum equals 0."** + +Same prefix sum + hash map approach. + +This demonstrates the general principle: any binary-counting problem can be reduced to "subarray sum = 0" via value mapping. + +* Multi-Category Balance — Equal A/B/C Counts [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Find the longest subarray with equal number of 'A's, 'B's, and 'C's. + +** Back +You can't use a single scalar (+1/-1) for three categories. Track **relative differences** between counts. + +Maintain running counts: c_A, c_B, c_C. +At each step, compute the tuple of differences: (c_A - c_B, c_B - c_C). + +If this tuple repeats later in the array, the elements between those indices have perfectly balanced A, B, C counts. + +Data structure: hash map where the key is the state tuple: + map, int> firstOccurrence; + +The tuple (diff_AB, diff_BC) captures the full relative state. If two positions share the same tuple, the subarray between them has zero net change in all three relative differences → equal counts. + +Time: O(n), Space: O(n) + +* Subarray Product Equals K — Prefix Product [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Find the number of subarrays with product equal to K. + +** Back +Use a **Prefix Product** array: + Product(i..j) = PrefixProduct[j] / PrefixProduct[i-1] + +Instead of subtracting, you divide. + +Data structure: hash map searching for current_product / K. + +Edge case: zeros reset the product to 0. Handle by: +1. Segmentation — split the array at zeros, solve each segment independently +2. Track position of last seen zero to reset boundaries + +Example: subarray product less than K (all positive): +#+begin_src c++ +int numSubarrayProductLessThanK(vector& nums, int k) { + if (k <= 1) return 0; + int count = 0, product = 1, left = 0; + for (int right = 0; right < nums.size(); right++) { + product *= nums[right]; + while (left <= right && product >= k) product /= nums[left++]; + count += right - left + 1; + } + return count; +} +#+end_src + +* Subarray Product Positive/Negative — Parity Tracking [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Find the maximum length of a subarray with a positive (or negative) product. + +** Back +Map: positive → +1, negative → -1, zero → resets the window. + +A subarray has positive product if it contains an **even** number of negatives. +A subarray has negative product if it contains an **odd** number of negatives. + +Track the parity (odd/even count) of negative numbers as you traverse: +- If parity is even at index i and was even at index j (j < i), the subarray (j+1..i) has positive product. +- If parity is odd at index i and was even at index j, the subarray (j+1..i) has negative product. + +Data structure: two hash maps — first occurrence of even-parity index and first occurrence of odd-parity index. + +Time: O(n), Space: O(n) + +* Subarray Bitwise XOR — Prefix XOR [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Find the number of subarrays with XOR equal to K. + +** Back +XOR is invertible (XOR is its own inverse), so the prefix pattern works: + XOR(i..j) = PrefixXOR[j] ^ PrefixXOR[i-1] + +Same hash map pattern as prefix sum: + +#+begin_src c++ +int subarrayXOR(vector& nums, int k) { + unordered_map prefixCount; + prefixCount[0] = 1; + int sum = 0, count = 0; + for (int num : nums) { + sum ^= num; + count += prefixCount[sum ^ k]; + prefixCount[sum]++; + } + return count; +} +#+end_src +Time: O(n), Space: O(n) + +* Subarray Bitwise OR/AND — Non-Invertible [algorithm:array] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +Can you use prefix sums for subarray problems with Bitwise OR or AND? + +** Back +**NO.** Unlike XOR, OR/AND are **not invertible**. You cannot "undo" an OR or AND operation. + +Exploit the key property: as you expand a subarray, the OR/AND result can only change at most **32 times** (for 32-bit integers) because bits only transition 0→1 (OR) or 1→0 (AND). + +Strategy: maintain a **set** of all possible OR results ending at the current index. The set never exceeds size 32. + +#+begin_src c++ +int subarrayBitwiseORs(vector& arr) { + unordered_set result, current; + for (int x : arr) { + unordered_set next; + next.insert(x); + for (int val : current) { + next.insert(val | x); + } + current = next; + for (int val : current) result.insert(val); + } + return result.size(); +} +#+end_src +Time: O(n * 32), Space: O(32) per step + +* Master Keyword-to-Algorithm Mapping [algorithm:interview] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +What is the master keyword-to-algorithm mapping for subarray problems? + +** Back +| Problem Phrase | Array Property | Algorithm | +|---------------|---------------|-----------| +| "Continuous subarray + Sum = K" | Only positive numbers | **Sliding Window** (O(1) space) | +| "Continuous subarray + Sum = K" | Positive & negative | **Prefix Sum + Hash Map** (O(n) space) | +| "Divisible by K" or "Multiple of X" | Any numbers | **Prefix Remainder + Hash Map** (sum % K) | +| "Equal number of X and Y" | Any numbers | **Value Mapping** (X→1, Y→-1) + Prefix Sum Map | +| "Maximum / Minimum Sum" | Any numbers | **Kadane's Algorithm** (DP) | +| "Subarray Sum + Frequent Updates" | Element mutations | **Fenwick Tree / Segment Tree** | +| "Subarray product = K" | No zeros | **Prefix Product** (division) | +| "Subarray product positive/negative" | Any numbers | **Parity tracking** of negative count | +| "Subarray XOR = K" | Any numbers | **Prefix XOR + Hash Map** | +| "Subarray OR / AND" | Any numbers | **Set of results** (bounded by 32 changes) | + +* Prefix State Generalization — The Unifying Concept [algorithm:concept] +:PROPERTIES: +:ANKI_NOTE_TYPE: Basic +:END: +** Front +What is the "prefix state" generalization that unifies all subarray patterns? + +** Back +The core philosophy of prefix sums is: **accumulate history as you traverse linearly, and use a hash map to track state.** + +| Problem | Mapping | Reduces To | +|---------|---------|-----------| +| Equal 0s and 1s | 0→-1, 1→+1 | Subarray sum = 0 | +| Equal odd/even | even→+1, odd→-1 | Subarray sum = 0 | +| Equal vowels/consonants | vowel→+1, consonant→-1 | Subarray sum = 0 | +| Equal A/B/C counts | Track (c_A-c_B, c_B-c_C) | Prefix state tuple repeats | +| Subarray product | Prefix product | Division (handle zeros) | +| Subarray XOR | Prefix XOR | XOR is invertible | +| Subarray sum | Prefix sum | Subtraction | + +The pattern: +1. Define a state that accumulates as you traverse +2. Find a way to "undo" or compare states (subtract, XOR, divide, compare tuples) +3. Use a hash map to find when the same state (or target difference) appears \ No newline at end of file diff --git a/org/questions/subarray-patterns.org b/org/questions/subarray-patterns.org index 723e61c..bb460fa 100644 --- a/org/questions/subarray-patterns.org +++ b/org/questions/subarray-patterns.org @@ -35,25 +35,25 @@ Instead of subtracting, you divide. While "subarray + sum" strongly hints at prefix sums, you need to look at the **constraints** and **types of numbers** to choose the absolute best tool. -| If you see "Subarray + Sum" AND... | The Real Pattern Is... | Why? | -|-------------------------------------|------------------------|------| -| All numbers are strictly positive (>= 0) and you need to find a target sum or max length. | **Sliding Window (Two Pointers)** | Because the window sum is monotonic (expanding always increases the sum; shrinking always decreases it). Sliding window optimizes space to O(1) compared to O(n) for prefix sums. | -| Numbers can be negative, or you need to find an exact target sum. | **Prefix Sum + Hash Map** | Monotonicity is broken. Adding an element could make the sum smaller, so sliding window fails. You *must* use a hash map to remember past states. | -| The array is **constantly being updated** (dynamic updates) between sum queries. | **Segment Tree or Fenwick Tree (BIT)** | A standard prefix sum array takes O(n) to update if an element changes. Segment/Fenwick trees drop update and query times to O(log n). | +| If you see "Subarray + Sum" AND... | The Real Pattern Is... | Why? | +|-------------------------------------------------------------------------------------------+--------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| All numbers are strictly positive (>= 0) and you need to find a target sum or max length. | **Sliding Window (Two Pointers)** | Because the window sum is monotonic (expanding always increases the sum; shrinking always decreases it). Sliding window optimizes space to O(1) compared to O(n) for prefix sums. | +| Numbers can be negative, or you need to find an exact target sum. | **Prefix Sum + Hash Map** | Monotonicity is broken. Adding an element could make the sum smaller, so sliding window fails. You *must* use a hash map to remember past states. | +| The array is **constantly being updated** (dynamic updates) between sum queries. | **Segment Tree or Fenwick Tree (BIT)** | A standard prefix sum array takes O(n) to update if an element changes. Segment/Fenwick trees drop update and query times to O(log n). | * Generalizing the Concept: "Prefix State" Don't limit this trick just to addition. The core philosophy of a prefix sum is **accumulating history as you traverse linearly**. You can use a hash map to track the "prefix state" for things that don't look like math at first. -| Problem | Mapping | Reduces To | -|---------|---------|-----------| -| Equal 0s and 1s | 0 → -1, 1 → +1 | Subarray sum = 0 | -| Equal odd/even | even → +1, odd → -1 | Subarray sum = 0 | -| Equal vowels/consonants | vowel → +1, consonant → -1 | Subarray sum = 0 | -| Equal A/B/C counts | Track (c_A - c_B, c_B - c_C) | Prefix state tuple repeats | -| Subarray product | Prefix product | Division (handle zeros) | -| Subarray XOR | Prefix XOR | XOR is invertible | -| Subarray sum | Prefix sum | Subtraction | +| Problem | Mapping | Reduces To | +|-------------------------+------------------------------+----------------------------| +| Equal 0s and 1s | 0 → -1, 1 → +1 | Subarray sum = 0 | +| Equal odd/even | even → +1, odd → -1 | Subarray sum = 0 | +| Equal vowels/consonants | vowel → +1, consonant → -1 | Subarray sum = 0 | +| Equal A/B/C counts | Track (c_A - c_B, c_B - c_C) | Prefix state tuple repeats | +| Subarray product | Prefix product | Division (handle zeros) | +| Subarray XOR | Prefix XOR | XOR is invertible | +| Subarray sum | Prefix sum | Subtraction | The pattern: 1. Define a state that accumulates as you traverse diff --git a/org/segment_tree.org b/org/segment_tree.org new file mode 100644 index 0000000..bf57346 --- /dev/null +++ b/org/segment_tree.org @@ -0,0 +1,152 @@ +#+title: Segment Tree + +* Segment +#+begin_src python +class seg_tree(): + def __init__(self, arr): + self.n = len(arr) + self.t = [0]*self.n + arr + for i in range(self.n-1, -1, -1): + self.t[i] = self.t[i<<1] + self.t[(i<<1)+1] + + def q(l, r): + +class Fenwick: + def __init__(self, lst: List[int], lamd): + self.n = len(lst) + self.t = [0] * self.n + lst + print(self.t) + for i in range(self.n-1, 1, -1): + self.t[i] = self.t[i<<1] + self.t[(i<<1)+1] + self.f = lamd + + def update(self, i, x): + i += self.n + self.t[i] = x + while i > 1: + self.t[i>>1] = self.t[i] + self.t[i^1] + i >>= 1 + + def query(self, lo, hi): + ans = 0 + lo += self.n + hi += self.n + while lo < hi: + if lo & 1: + ans = min(ans, self.t[lo]) + lo += 1 + + if hi & 1: + hi -= 1 + ans = min(ans, self.t[hi]) + lo >>= 1 + hi >>= 1 + return ans + + +#+end_src + +#+begin_src python :results output +class Fenwick: + def __init__(self, lst: list[int]): + self.n = len(lst) + self.t = [float('inf')] * self.n + lst + for i in range(self.n-1, 1, -1): + self.t[i] = min(self.t[i<<1], self.t[(i<<1)+1]) + print(self.t) + + def update(self, i, x): + i += self.n + self.t[i] = x + while i > 1: + self.t[i>>1] = self.t[i] + self.t[i^1] + i >>= 1 + + def query(self, lo, hi): + ans = 0 + lo += self.n + hi += self.n + while lo < hi: + if lo & 1: + ans = min(ans, self.t[lo]) + lo += 1 + + if hi & 1: + hi -= 1 + ans = min(ans, self.t[hi]) + lo >>= 1 + hi >>= 1 + return ans +fw = Fenwick([999,2,1,999,999,999,999]) +i = fw.query(0, 2) +print(i) +#+end_src + +#+RESULTS: +: [inf, inf, 1, 999, 1, 999, 999, 999, 2, 1, 999, 999, 999, 999] +: 0 + +#+begin_src python :results output +class Fenwick: + def __init__(self, lst): + self.n = len(lst) + self.t = [float('inf')] * self.n + lst + for i in range(self.n - 1, 0, -1): # include root + self.t[i] = min(self.t[i<<1], self.t[(i<<1)+1]) + + def update(self, i, x): + i += self.n + self.t[i] = x + while i > 1: + self.t[i>>1] = min(self.t[i], self.t[i^1]) # min, not + + i >>= 1 + + def query(self, lo, hi): + ans = float('inf') # min identity + lo += self.n; hi += self.n + while lo < hi: + if lo & 1: ans = min(ans, self.t[lo]); lo += 1 + if hi & 1: hi -= 1; ans = min(ans, self.t[hi]) + lo >>= 1; hi >>= 1 + return ans +fw = Fenwick([999,2,1,999,999,999,999]) +i = fw.query(0, 3) +print(i) +#+end_src + +#+RESULTS: +: 1 + +#+begin_src markdown +The code you provided is actually a *Segment Tree*, not a Fenwick Tree. It works by using a compact array representation of a complete binary tree to perform range minimum queries. + +,*** 1. Structure +- *Array Layout:* For an input list of size $n$, it creates an array =t= of size $2n$. + ,* Indices =n= to =2n-1= store the original elements (leaves). + ,* Indices =1= to =n-1= store the minimum of their children (internal nodes). +- *Parent-Child Logic:* For any node at index =i=: + ,* Left child: =i << 1= (or =2i=) + ,* Right child: =(i << 1) + 1= (or =2i + 1=) + ,* Parent: =i >> 1= (or =i // 2=) + +,*** 2. Why it works + +,**** Initialization (=__init__=) +It populates the leaves first, then iterates backwards from =n-1= down to =1=. This ensures that when calculating the minimum for node =i=, its children (=2i= and =2i+1=) have already been processed (or are leaves). + +,**** Point Update (=update=) +1. Updates the leaf at =i + n=. +2. Moves up the tree (=i >>= 1=), re-calculating the minimum of the current node by comparing the updated node with its sibling (=i ^ 1=). This propagates the change up to the root in $O(\log n)$ time. + +,**** Range Query (=query=) +This uses a "bottom-up" approach on the interval =[lo, hi)=: +1. If =lo= is a right child (=lo & 1=), it's the only child of its parent inside the range. We include it in the minimum and move to the next subtree (=lo += 1=). +2. If =hi= is a right child, the element at =hi-1= is inside the range. We include it and move left. +3. We then move both pointers to their parents (=>> 1=). +4. This collects only the "minimal" set of nodes covering the range in $O(\log n)$ time. + +,*** Summary +While a *Fenwick Tree* (Binary Indexed Tree) is usually used for prefix sums and handles point updates in $O(\log n)$, it cannot efficiently perform range *minimum* queries for arbitrary ranges. This *Segment Tree* does exactly that by explicitly storing the minimum of power-of-two blocks. + +,*** +#+end_src