--- excalidraw-plugin: parsed tags: - excalidraw type: mixed --- ==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving' # Code Block The code provided is designed to solve the problem of finding all possible matches of a given plaintext pattern `p` in an encrypted ciphertext `v`, where the ciphertext is a result of shifting the letters of `p` by an unknown shift `k` using the Caesar cipher. To accomplish this efficiently, the solution employs the **Knuth-Morris-Pratt (KMP)** algorithm. Let’s walk through the solution and explain how the KMP algorithm is utilized, along with an example. ### How the KMP Algorithm Works The **Knuth-Morris-Pratt (KMP)** algorithm is an efficient string-searching algorithm that searches for occurrences of a "pattern" (substring) within a "text" (larger string) in \( O(N + M) \) time, where \( N \) is the length of the text and \( M \) is the length of the pattern. It is particularly effective because it avoids unnecessary re-comparisons, thus speeding up the search process. The key idea is to preprocess the pattern and create an auxiliary array called the **Longest Prefix Suffix (LPS)** array. This array helps determine the next position to match in the pattern without having to re-check previously matched characters. #### Steps of the KMP Algorithm 1. **Construct the LPS Array**: - The LPS array is created based on the pattern. The value at each index in the LPS array indicates the length of the longest proper prefix which is also a suffix for the substring ending at that index. - This array is used to skip unnecessary comparisons when a mismatch occurs during the search. 2. **Search Using the LPS Array**: - The algorithm uses the LPS array to efficiently search for the pattern in the text. - When characters match, both pointers (`i` for text and `j` for pattern) move forward. - If a mismatch occurs, the pattern pointer `j` is moved back using the LPS array, allowing the search to continue without revisiting characters in the text unnecessarily. ### Code Explanation Here’s how the code applies the KMP algorithm: 1. **Distance Calculation**: - First, it calculates the distances (differences between consecutive characters modulo 26) for both the ciphertext `v` and the pattern `p`. - This transformation makes it easier to find all shifts `k` that match `p` in `v` because matching distances between characters is equivalent to matching a shift of `p` in `v`. 2. **KMP Search**: - The `KMPSearch` function is called with the distance arrays for `v` and `p`. - It first computes the LPS array for the pattern's distances using the `computeLPSArray` function. - The search then uses this LPS array to efficiently find all occurrences of the pattern's distance array within the ciphertext's distance array. Each occurrence corresponds to a starting index where some shift of `p` matches a substring of `v`. ### Example Walkthrough Let’s walk through the provided example: ``` 69 7 DROBOKBODGYPEXXIGYBNCXKWOVIKLTEBOBKXNXYGROBOXNDROIIOXMBIZDDYOKMRYDROB NOWHERE ``` 1. **Pattern Length and Ciphertext Length**: - The pattern `p` is "NOWHERE" of length 7. - The ciphertext `v` is of length 69. 2. **Calculate Distance Arrays**: - For the pattern `"NOWHERE"`, the distances between consecutive characters are calculated: ``` Distances: [(O - N + 26) % 26, (W - O + 26) % 26, ..., (E - R + 26) % 26] ``` This results in an integer array representing the differences modulo 26. - Similarly, the distance array for the ciphertext is calculated. 3. **Perform KMP Search**: - The `KMPSearch` function processes the pattern’s distance array and the ciphertext’s distance array using the KMP algorithm. - The LPS array is constructed based on the pattern's distance array. It is used to skip unnecessary comparisons and quickly identify matching substrings. - The algorithm finds matches for the pattern's distance array within the ciphertext's distance array, indicating where any shift \( S_k(p) \) matches a substring of `v`. ### Example Output For the example provided, the output is: ``` 27 37 ``` - The algorithm finds that when `k = 23`, `S23(NOWHERE) = KLTEBOB` matches a substring of `v` starting at index 27. - When `k = 10`, `S10(NOWHERE) = XYGROBO` matches a substring of `v` starting at index 37. ### Key Points on Efficiency - **Time Complexity**: The KMP algorithm runs in \( O(N + M) \), which is efficient enough given the constraints (both \( N \) and \( M \) can be very large). The preprocessing step (LPS array computation) and the search itself are both linear. - **Space Complexity**: The LPS array takes \( O(M) \) space, and the distance arrays for the pattern and the text take \( O(M) \) and \( O(N) \) space, respectively. ### Summary The code effectively transforms the problem of finding shifts in the Caesar cipher into a problem of matching distance arrays. By leveraging the KMP algorithm, it efficiently finds all possible occurrences of the pattern (under any shift) in the ciphertext. This approach ensures that the solution is both time and space efficient, making it suitable for large inputs as described in the constraints. # Code Block 1 ``` The code provided solves the problem using a **hashmap** data structure to efficiently store and retrieve information about people and their relationships, and recursively prints family trees based on queries. Let’s walk through the solution step by step and highlight how the hashmap is central to its functionality. ### Overview 1. **Input Reading and Parsing**: - The program first reads the number of entries `n` in the dataset. Each entry contains a pair of names indicating a parent-child relationship. - These names are parsed and stored as `Person` structures, which are then inserted into a hashmap for quick retrieval. 2. **Data Storage**: - A `HashMap` structure is used to store and retrieve `Person` objects efficiently. This allows the program to quickly check if a person has already been recorded or if a new person needs to be created. 3. **Building the Family Tree**: - For each parent-child relationship, the program finds or creates the `Person` structure for both the parent and the child and establishes the relationship by adding the child to the parent’s list of children. 4. **Handling Queries**: - The program reads each query and retrieves the corresponding `Person` from the hashmap. It then recursively prints the family tree for that person. ### Hashmap Implementation The hashmap is implemented using a simple **chained hashing** technique to efficiently store and retrieve the `Person` objects. Here’s how it works: 1. **Hash Function** (`hash`): - The `hash` function (`djb2` hash) computes a hash value for each name (string). It combines the characters of the name using arithmetic operations to generate a unique (or mostly unique) hash value. This hash value is then used as an index into the hashmap’s array (bucket). - The function ensures that names are distributed across the buckets of the hashmap. 2. **HashMap Structure** (`HashMap`): - The `HashMap` structure contains an array of pointers (`buckets`) to `HashMapEntry` structures. Each bucket represents a linked list (a chain) of entries that correspond to different names hashing to the same bucket index. - The hashmap is initialized with a predefined size (`HASH_MAP_SIZE = 131071`), which is a prime number to minimize collisions. 3. **Insertion** (`hashmapInsert`): - When inserting a `Person` into the hashmap, the hash value of the name is computed and used to locate the appropriate bucket. - If the bucket already has entries (other people who hashed to the same bucket), the code checks if the name already exists (to avoid duplication) or inserts the new `Person` object at the start of the chain. - The hashmap handles collisions by chaining: if two names have the same hash value, they are stored in a linked list within the same bucket. 4. **Retrieval** (`hashmapGet`): - When the program needs to retrieve a `Person` object, it computes the hash of the name and looks up the corresponding bucket. - It then traverses the linked list in the bucket to find the entry with the matching name. If found, it returns the `Person` object; otherwise, it returns `NULL`. ### How the HashMap Facilitates Efficient Operations The hashmap enables **constant-time** (on average) insertion and retrieval of `Person` objects, which is crucial given the constraints (up to 75,000 entries and 500,000 queries). Here’s how it fits into the larger solution: 1. **Quick Lookup**: - When processing each parent-child relationship, the program needs to quickly check if the parent or child already exists. The hashmap allows for these checks to be performed in constant time. 2. **Efficient Insertion**: - If a `Person` object does not yet exist in the hashmap, it is created and inserted efficiently using the computed hash value. 3. **Fast Query Handling**: - When a query is processed, the program retrieves the `Person` object corresponding to the queried name in constant time (on average) and recursively prints their descendants. ### Building the Family Tree - The `addChild` function adds a child to a parent’s `children` list, and the list is dynamically resized if needed. - The `printFamilyTree` function recursively prints each person and their children in alphabetical order. The `qsort` function is used to sort the children before printing. ### Summary The code effectively solves the problem by using a hashmap to: - Store `Person` objects and establish parent-child relationships quickly. - Enable constant-time lookup of persons, which is crucial given the large input size. - Efficiently handle queries by retrieving the root person and recursively printing their family tree. This approach ensures the solution is optimized for time complexity (O(1) on average for insertion and retrieval) and efficiently handles the constraints provided in the problem statement. ``` # Code Block 2 The code provided solves the problem using a **hashmap** data structure to efficiently store and retrieve information about people and their relationships, and recursively prints family trees based on queries. Let’s walk through the solution step by step and highlight how the hashmap is central to its functionality. ### Overview 1. **Input Reading and Parsing**: - The program first reads the number of entries `n` in the dataset. Each entry contains a pair of names indicating a parent-child relationship. - These names are parsed and stored as `Person` structures, which are then inserted into a hashmap for quick retrieval. 2. **Data Storage**: - A `HashMap` structure is used to store and retrieve `Person` objects efficiently. This allows the program to quickly check if a person has already been recorded or if a new person needs to be created. 3. **Building the Family Tree**: - For each parent-child relationship, the program finds or creates the `Person` structure for both the parent and the child and establishes the relationship by adding the child to the parent’s list of children. 4. **Handling Queries**: - The program reads each query and retrieves the corresponding `Person` from the hashmap. It then recursively prints the family tree for that person. ### Hashmap Implementation The hashmap is implemented using a simple **chained hashing** technique to efficiently store and retrieve the `Person` objects. Here’s how it works: 1. **Hash Function** (`hash`): - The `hash` function (`djb2` hash) computes a hash value for each name (string). It combines the characters of the name using arithmetic operations to generate a unique (or mostly unique) hash value. This hash value is then used as an index into the hashmap’s array (bucket). - The function ensures that names are distributed across the buckets of the hashmap. 2. **HashMap Structure** (`HashMap`): - The `HashMap` structure contains an array of pointers (`buckets`) to `HashMapEntry` structures. Each bucket represents a linked list (a chain) of entries that correspond to different names hashing to the same bucket index. - The hashmap is initialized with a predefined size (`HASH_MAP_SIZE = 131071`), which is a prime number to minimize collisions. 3. **Insertion** (`hashmapInsert`): - When inserting a `Person` into the hashmap, the hash value of the name is computed and used to locate the appropriate bucket. - If the bucket already has entries (other people who hashed to the same bucket), the code checks if the name already exists (to avoid duplication) or inserts the new `Person` object at the start of the chain. - The hashmap handles collisions by chaining: if two names have the same hash value, they are stored in a linked list within the same bucket. 4. **Retrieval** (`hashmapGet`): - When the program needs to retrieve a `Person` object, it computes the hash of the name and looks up the corresponding bucket. - It then traverses the linked list in the bucket to find the entry with the matching name. If found, it returns the `Person` object; otherwise, it returns `NULL`. ### How the HashMap Facilitates Efficient Operations The hashmap enables **constant-time** (on average) insertion and retrieval of `Person` objects, which is crucial given the constraints (up to 75,000 entries and 500,000 queries). Here’s how it fits into the larger solution: 1. **Quick Lookup**: - When processing each parent-child relationship, the program needs to quickly check if the parent or child already exists. The hashmap allows for these checks to be performed in constant time. 2. **Efficient Insertion**: - If a `Person` object does not yet exist in the hashmap, it is created and inserted efficiently using the computed hash value. 3. **Fast Query Handling**: - When a query is processed, the program retrieves the `Person` object corresponding to the queried name in constant time (on average) and recursively prints their descendants. ### Building the Family Tree - The `addChild` function adds a child to a parent’s `children` list, and the list is dynamically resized if needed. - The `printFamilyTree` function recursively prints each person and their children in alphabetical order. The `qsort` function is used to sort the children before printing. ### Summary The code effectively solves the problem by using a hashmap to: - Store `Person` objects and establish parent-child relationships quickly. - Enable constant-time lookup of persons, which is crucial given the large input size. - Efficiently handle queries by retrieving the root person and recursively printing their family tree. This approach ensures the solution is optimized for time complexity (O(1) on average for insertion and retrieval) and efficiently handles the constraints provided in the problem statement. # example Sure, let's use a simpler example to illustrate how the KMP algorithm works in the context of the problem. We'll use shorter strings to make the process clearer. ### Example Let's say we have the following input: ``` 10 3 ABCDEFABCD BCD ``` Here: - The ciphertext `v` is `"ABCDEFABCD"` of length 10. - The pattern `p` is `"BCD"` of length 3. ### Step 1: Calculate the Distance Arrays First, we convert both the pattern and the ciphertext into **distance arrays**. This helps us match shifts of the pattern in the ciphertext regardless of the value of \( k \). For simplicity, let's look at how these arrays are computed: - **Pattern `"BCD"`**: - Differences between consecutive characters: ``` (C - B) % 26 = 1 (D - C) % 26 = 1 ``` - The distance array for the pattern is: `[1, 1]` - **Ciphertext `"ABCDEFABCD"`**: - Differences between consecutive characters: ``` (B - A) % 26 = 1 (C - B) % 26 = 1 (D - C) % 26 = 1 (E - D) % 26 = 1 (F - E) % 26 = 1 (A - F + 26) % 26 = 21 (B - A) % 26 = 1 (C - B) % 26 = 1 (D - C) % 26 = 1 ``` - The distance array for the ciphertext is: `[1, 1, 1, 1, 1, 21, 1, 1, 1]` ### Step 2: Run the KMP Search Now, we use the **KMP algorithm** to find occurrences of the pattern’s distance array (`[1, 1]`) in the ciphertext’s distance array (`[1, 1, 1, 1, 1, 21, 1, 1, 1]`). #### KMP Steps: 1. **Compute the LPS Array for the Pattern**: - The LPS (Longest Prefix Suffix) array for `[1, 1]` is `[0, 1]`. This indicates: - At index 0, the longest proper prefix that is also a suffix is of length 0. - At index 1, the longest proper prefix that is also a suffix is of length 1. 2. **Search in the Ciphertext**: - We start with `i = 0` (text pointer) and `j = 0` (pattern pointer). - As we go through the ciphertext’s distance array: - At `i = 0`, `txt[i]` matches `pat[j]` (`1 == 1`), so we increment both `i` and `j`. - At `i = 1`, `txt[i]` matches `pat[j]` (`1 == 1`), so we increment both `i` and `j`. - Now, `j` equals the length of the pattern (2), indicating a match at index `i - j = 0`. - We then use the LPS array to update `j = lps[j - 1] = 1` and continue searching. - This process repeats, finding another match at index `1`, then at index `2`, then at index `3`, and finally at index `7`. ### Step 3: Output the Matches The KMP algorithm finds the following indices where the pattern `"BCD"` (under any shift) matches a substring of `"ABCDEFABCD"`: ``` 0 1 2 3 7 ``` These indices are printed as the output. ### Summary of the Example 1. **Distance Conversion**: Both the pattern and the ciphertext are converted to arrays of distances modulo 26. 2. **KMP Search**: The KMP algorithm efficiently finds all occurrences of the pattern's distance array within the ciphertext's distance array. 3. **Matches Found**: The output is the list of starting indices where the pattern matches the ciphertext. This simpler example shows how the KMP algorithm uses the distance transformation to efficiently search for pattern matches across any Caesar shift. # Kruskal {"type":"excalidraw/clipboard","elements":[{"id":"BX4pIeu1","type":"text","x":528.6298973446837,"y":-1119.9324156746045,"width":820.8592529296875,"height":1975,"angle":0,"strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"strokeStyle":"solid","roughness":1,"opacity":100,"groupIds":[],"frameId":null,"index":"b0u","roundness":null,"seed":1386352415,"version":2,"versionNonce":731985489,"isDeleted":false,"boundElements":null,"updated":1729612303163,"link":null,"locked":false,"text":"Sure! Let's go through Kruskal's algorithm step by step and explain all the terms\nin simple language, so you understand how the code works and what each part\ndoes.\n\n### Overview of Kruskal's Algorithm\nKruskal’s algorithm is used to find the **Minimum Spanning Tree (MST)** of a\ngraph. An MST connects all the nodes (components) in the graph using the\nminimum possible total weight of edges, ensuring no cycles (loops) are formed.\nIt’s a greedy algorithm because it always chooses the smallest edge available\nthat doesn't create a cycle.\n\n### Step-by-Step Breakdown\n\n1. **Sorting Edges by Weight**:\n- The algorithm starts by sorting all the edges (interconnects between\ncomponents) based on their weight (cost). The idea is to work from the\ncheapest to the most expensive edges to ensure the total cost of the\nconnections remains minimal.\n\n2. **Union-Find Data Structure (Disjoint Set Union or DSU)**:\n- **Union-Find** (or **Disjoint Set Union**) is a way to keep track of which\ncomponents are connected as the algorithm progresses. It helps us determine if\ntwo components are already connected and ensures we don’t create cycles when\nconnecting them.\n- It does this by grouping components into \"sets.\" Each component in the\ngraph starts as its own set, and as edges are added, sets are merged.\n\n3. **Path Compression**:\n- **Path Compression** is a technique used in the union-find structure to\nspeed up the process of finding the \"root\" or \"leader\" of a set. It works by\ndirectly linking components to their ultimate leader, making future lookups much\nquicker.\n- Think of it like quickly navigating to the head of an organization without\nneeding to go through each level.\n\n4. **Union by Rank**:\n- **Union by Rank** is another technique used in the union-find structure to\nkeep the sets balanced. It helps decide which set (or tree) should be attached\nto which when merging two sets.\n- It looks at the \"rank\" or \"height\" of the trees representing the sets. The\nsmaller tree is always attached to the larger one to keep the structure balanced,\npreventing it from becoming too tall and thus slower to navigate.\n\n5. **Constructing the MST**:\n- The algorithm goes through each edge (starting from the smallest) and\nchecks if it can connect two components without forming a cycle:\n- If the two components are not already connected (i.e., they belong to\ndifferent sets), it merges these sets using the union-find structure and adds the\nedge to the MST.\n- If the components are already connected (they belong to the same set),\nthe algorithm skips the edge to avoid creating a cycle.\n - This process continues until the MST has exactly \\( n - 1 \\) edges, where \\(\nn \\) is the number of components. This is because, for a graph with \\( n \\)\nnodes, an MST will always have \\( n - 1 \\) edges.\n\n### Important Concepts and Terms Simplified\n\n- **Minimum Spanning Tree (MST)**: The cheapest way to connect all components\n(nodes) with the minimum total cost without creating cycles.\n- **Edge**: A connection between two components with an associated weight\n(cost).\n- **Node**: A component or point that we want to connect (like a transistor or\nresistor on Ali's chip).\n- **Cycle**: A loop in a graph where you can start from a node, follow edges, and\nget back to the same node. In an MST, cycles are not allowed.\n- **Union-Find (Disjoint Set Union, DSU)**: A method to group nodes into sets\nand quickly determine if two nodes belong to the same set (connected group). It\nhelps avoid cycles and ensures we build the MST correctly.\n\n### Summary of the Algorithm\n1. Sort all edges by cost.\n2. Initialize each component as its own set using the union-find structure.\n3. Go through the sorted edges and add them to the MST if they connect\ndifferent sets (no cycle formed).\n4. Stop when \\( n - 1 \\) edges are added, completing the MST.\n\nBy efficiently managing which nodes are connected using the union-find structure\nwith path compression and union by rank, Kruskal’s algorithm ensures the minimum\ntotal cost to connect all components while avoiding cycles.","rawText":"Sure! Let's go through Kruskal's algorithm step by step and explain all the terms in simple language, so you understand how the code works and what each part does.\n\n### Overview of Kruskal's Algorithm\nKruskal’s algorithm is used to find the **Minimum Spanning Tree (MST)** of a graph. An MST connects all the nodes (components) in the graph using the minimum possible total weight of edges, ensuring no cycles (loops) are formed. It’s a greedy algorithm because it always chooses the smallest edge available that doesn't create a cycle.\n\n### Step-by-Step Breakdown\n\n1. **Sorting Edges by Weight**:\n - The algorithm starts by sorting all the edges (interconnects between components) based on their weight (cost). The idea is to work from the cheapest to the most expensive edges to ensure the total cost of the connections remains minimal.\n\n2. **Union-Find Data Structure (Disjoint Set Union or DSU)**:\n - **Union-Find** (or **Disjoint Set Union**) is a way to keep track of which components are connected as the algorithm progresses. It helps us determine if two components are already connected and ensures we don’t create cycles when connecting them.\n - It does this by grouping components into \"sets.\" Each component in the graph starts as its own set, and as edges are added, sets are merged.\n\n3. **Path Compression**:\n - **Path Compression** is a technique used in the union-find structure to speed up the process of finding the \"root\" or \"leader\" of a set. It works by directly linking components to their ultimate leader, making future lookups much quicker.\n - Think of it like quickly navigating to the head of an organization without needing to go through each level.\n\n4. **Union by Rank**:\n - **Union by Rank** is another technique used in the union-find structure to keep the sets balanced. It helps decide which set (or tree) should be attached to which when merging two sets.\n - It looks at the \"rank\" or \"height\" of the trees representing the sets. The smaller tree is always attached to the larger one to keep the structure balanced, preventing it from becoming too tall and thus slower to navigate.\n\n5. **Constructing the MST**:\n - The algorithm goes through each edge (starting from the smallest) and checks if it can connect two components without forming a cycle:\n - If the two components are not already connected (i.e., they belong to different sets), it merges these sets using the union-find structure and adds the edge to the MST.\n - If the components are already connected (they belong to the same set), the algorithm skips the edge to avoid creating a cycle.\n - This process continues until the MST has exactly \\( n - 1 \\) edges, where \\( n \\) is the number of components. This is because, for a graph with \\( n \\) nodes, an MST will always have \\( n - 1 \\) edges.\n\n### Important Concepts and Terms Simplified\n\n- **Minimum Spanning Tree (MST)**: The cheapest way to connect all components (nodes) with the minimum total cost without creating cycles.\n- **Edge**: A connection between two components with an associated weight (cost).\n- **Node**: A component or point that we want to connect (like a transistor or resistor on Ali's chip).\n- **Cycle**: A loop in a graph where you can start from a node, follow edges, and get back to the same node. In an MST, cycles are not allowed.\n- **Union-Find (Disjoint Set Union, DSU)**: A method to group nodes into sets and quickly determine if two nodes belong to the same set (connected group). It helps avoid cycles and ensures we build the MST correctly.\n\n### Summary of the Algorithm\n1. Sort all edges by cost.\n2. Initialize each component as its own set using the union-find structure.\n3. Go through the sorted edges and add them to the MST if they connect different sets (no cycle formed).\n4. Stop when \\( n - 1 \\) edges are added, completing the MST.\n\nBy efficiently managing which nodes are connected using the union-find structure with path compression and union by rank, Kruskal’s algorithm ensures the minimum total cost to connect all components while avoiding cycles.","fontSize":20,"fontFamily":5,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Sure! Let's go through Kruskal's algorithm step by step and explain all the terms in simple language, so you understand how the code works and what each part does.\n\n### Overview of Kruskal's Algorithm\nKruskal’s algorithm is used to find the **Minimum Spanning Tree (MST)** of a graph. An MST connects all the nodes (components) in the graph using the minimum possible total weight of edges, ensuring no cycles (loops) are formed. It’s a greedy algorithm because it always chooses the smallest edge available that doesn't create a cycle.\n\n### Step-by-Step Breakdown\n\n1. **Sorting Edges by Weight**:\n - The algorithm starts by sorting all the edges (interconnects between components) based on their weight (cost). The idea is to work from the cheapest to the most expensive edges to ensure the total cost of the connections remains minimal.\n\n2. **Union-Find Data Structure (Disjoint Set Union or DSU)**:\n - **Union-Find** (or **Disjoint Set Union**) is a way to keep track of which components are connected as the algorithm progresses. It helps us determine if two components are already connected and ensures we don’t create cycles when connecting them.\n - It does this by grouping components into \"sets.\" Each component in the graph starts as its own set, and as edges are added, sets are merged.\n\n3. **Path Compression**:\n - **Path Compression** is a technique used in the union-find structure to speed up the process of finding the \"root\" or \"leader\" of a set. It works by directly linking components to their ultimate leader, making future lookups much quicker.\n - Think of it like quickly navigating to the head of an organization without needing to go through each level.\n\n4. **Union by Rank**:\n - **Union by Rank** is another technique used in the union-find structure to keep the sets balanced. It helps decide which set (or tree) should be attached to which when merging two sets.\n - It looks at the \"rank\" or \"height\" of the trees representing the sets. The smaller tree is always attached to the larger one to keep the structure balanced, preventing it from becoming too tall and thus slower to navigate.\n\n5. **Constructing the MST**:\n - The algorithm goes through each edge (starting from the smallest) and checks if it can connect two components without forming a cycle:\n - If the two components are not already connected (i.e., they belong to different sets), it merges these sets using the union-find structure and adds the edge to the MST.\n - If the components are already connected (they belong to the same set), the algorithm skips the edge to avoid creating a cycle.\n - This process continues until the MST has exactly \\( n - 1 \\) edges, where \\( n \\) is the number of components. This is because, for a graph with \\( n \\) nodes, an MST will always have \\( n - 1 \\) edges.\n\n### Important Concepts and Terms Simplified\n\n- **Minimum Spanning Tree (MST)**: The cheapest way to connect all components (nodes) with the minimum total cost without creating cycles.\n- **Edge**: A connection between two components with an associated weight (cost).\n- **Node**: A component or point that we want to connect (like a transistor or resistor on Ali's chip).\n- **Cycle**: A loop in a graph where you can start from a node, follow edges, and get back to the same node. In an MST, cycles are not allowed.\n- **Union-Find (Disjoint Set Union, DSU)**: A method to group nodes into sets and quickly determine if two nodes belong to the same set (connected group). It helps avoid cycles and ensures we build the MST correctly.\n\n### Summary of the Algorithm\n1. Sort all edges by cost.\n2. Initialize each component as its own set using the union-find structure.\n3. Go through the sorted edges and add them to the MST if they connect different sets (no cycle formed).\n4. Stop when \\( n - 1 \\) edges are added, completing the MST.\n\nBy efficiently managing which nodes are connected using the union-find structure with path compression and union by rank, Kruskal’s algorithm ensures the minimum total cost to connect all components while avoiding cycles.","autoResize":false,"lineHeight":1.25}],"files":{}} # Code Block 3 ### Overview of Kruskal's Algorithm Kruskal’s algorithm is used to find the **Minimum Spanning Tree (MST)** of a graph. An MST connects all the nodes (components) in the graph using the minimum possible total weight of edges, ensuring no cycles (loops) are formed. It’s a greedy algorithm because it always chooses the smallest edge available that doesn't create a cycle. ### Step-by-Step Breakdown 1. **Sorting Edges by Weight**: - The algorithm starts by sorting all the edges (interconnects between components) based on their weight (cost). The idea is to work from the cheapest to the most expensive edges to ensure the total cost of the connections remains minimal. 2. **Union-Find Data Structure (Disjoint Set Union or DSU)**: - **Union-Find** (or **Disjoint Set Union**) is a way to keep track of which components are connected as the algorithm progresses. It helps us determine if two components are already connected and ensures we don’t create cycles when connecting them. - It does this by grouping components into "sets." Each component in the graph starts as its own set, and as edges are added, sets are merged. 3. **Path Compression**: - **Path Compression** is a technique used in the union-find structure to speed up the process of finding the "root" or "leader" of a set. It works by directly linking components to their ultimate leader, making future lookups much quicker. - Think of it like quickly navigating to the head of an organization without needing to go through each level. 4. **Union by Rank**: - **Union by Rank** is another technique used in the union-find structure to keep the sets balanced. It helps decide which set (or tree) should be attached to which when merging two sets. - It looks at the "rank" or "height" of the trees representing the sets. The smaller tree is always attached to the larger one to keep the structure balanced, preventing it from becoming too tall and thus slower to navigate. 5. **Constructing the MST**: - The algorithm goes through each edge (starting from the smallest) and checks if it can connect two components without forming a cycle: - If the two components are not already connected (i.e., they belong to different sets), it merges these sets using the union-find structure and adds the edge to the MST. - If the components are already connected (they belong to the same set), the algorithm skips the edge to avoid creating a cycle. - This process continues until the MST has exactly \( n - 1 \) edges, where \( n \) is the number of components. This is because, for a graph with \( n \) nodes, an MST will always have \( n - 1 \) edges. ### Important Concepts and Terms Simplified - **Minimum Spanning Tree (MST)**: The cheapest way to connect all components (nodes) with the minimum total cost without creating cycles. - **Edge**: A connection between two components with an associated weight (cost). - **Node**: A component or point that we want to connect (like a transistor or resistor on Ali's chip). - **Cycle**: A loop in a graph where you can start from a node, follow edges, and get back to the same node. In an MST, cycles are not allowed. - **Union-Find (Disjoint Set Union, DSU)**: A method to group nodes into sets and quickly determine if two nodes belong to the same set (connected group). It helps avoid cycles and ensures we build the MST correctly. ### Summary of the Algorithm 1. Sort all edges by cost. 2. Initialize each component as its own set using the union-find structure. 3. Go through the sorted edges and add them to the MST if they connect different sets (no cycle formed). 4. Stop when \( n - 1 \) edges are added, completing the MST. By efficiently managing which nodes are connected using the union-find structure with path compression and union by rank, Kruskal’s algorithm ensures the minimum total cost to connect all components while avoiding cycles. # Code Block 4 To apply Kruskal's algorithm to Ali's computer chip design problem, we treat the components of the chip as **nodes** in a graph and the potential interconnects between them as **edges** with associated costs. The goal is to find the **Minimum Spanning Tree (MST)**, which connects all components with the minimum total cost. Here’s how the code applies Kruskal’s algorithm to solve this: ### Step-by-Step Application 1. **Input Parsing**: - The code first reads the number of components (`n`) and the number of potential interconnects (`m`). - It then reads each interconnect, which includes two components (`a` and `b`) and the cost (`c`) of connecting them. - The components are converted to **0-based indices** (if they start from 1) for easier handling in arrays. 2. **Store Edges**: - Each interconnect is stored as an **edge**. An edge contains: - `s`: The starting component (node). - `e`: The ending component (node). - `w`: The weight (cost) of the interconnect. - All edges are stored in an array called `edges`. 3. **Sort the Edges by Cost**: - The code uses `qsort` to sort the edges in ascending order based on their weights. This ensures that the algorithm starts by considering the cheapest edges first, which is a core part of Kruskal's greedy strategy. 4. **Initialize Union-Find Structure**: - The algorithm initializes two arrays: - `parent`: Keeps track of the "leader" of each set. Initially, each component is its own parent. - `rank`: Helps manage the height of the trees in the union-find structure. Initially, all ranks are zero because each node is its own set. - These structures help efficiently determine whether adding an edge creates a cycle and manage merging of sets when adding edges. 5. **Iterate Through the Sorted Edges**: - The algorithm iterates through the sorted edges and uses the union-find methods (`find` and `setUnion`) to determine if an edge can be added to the MST: - **Check if the Edge Forms a Cycle**: - The `find` function checks if the two components connected by an edge already belong to the same set. If they do, adding the edge would form a cycle, so it is skipped. - If the components are in different sets, the `setUnion` function merges them, effectively connecting these components without forming a cycle. - **Add the Edge to the MST**: - If the edge connects two different sets, it is added to the MST, and its weight is accumulated to the total MST cost. - The algorithm continues until it has added exactly \( n-1 \) edges, which is the number needed to connect all nodes without cycles. 6. **Return the Total Cost**: - Once the MST is complete (i.e., all components are connected), the algorithm returns the total cost of all edges included in the MST. ### Example Walkthrough Let’s say Ali has 4 components and 5 potential interconnects: ``` n = 4, m = 5 Interconnects: 1. (1, 2, 3) 2. (1, 3, 1) 3. (2, 3, 2) 4. (2, 4, 4) 5. (3, 4, 5) ``` 1. **Convert Input to 0-Based Indexing**: - Components are converted: `(0, 1, 3)`, `(0, 2, 1)`, `(1, 2, 2)`, `(1, 3, 4)`, `(2, 3, 5)`. 2. **Sort the Edges by Cost**: - After sorting, the edges are: `(0, 2, 1)`, `(1, 2, 2)`, `(0, 1, 3)`, `(1, 3, 4)`, `(2, 3, 5)`. 3. **Initialize Union-Find**: - Initially, each component is its own set (`parent` array: `[0, 1, 2, 3]`), and the ranks are all zero (`rank` array: `[0, 0, 0, 0]`). 4. **Process Edges**: - The algorithm processes the edges in order: - Edge `(0, 2, 1)`: This connects different sets (component 0 and component 2). It is added to the MST. - Edge `(1, 2, 2)`: This connects component 1 and component 2, which are not yet connected. It is added to the MST. - Edge `(0, 1, 3)`: This connects component 0 and component 1, which are still not connected. It is added to the MST. At this point, the MST has \( n-1 = 3 \) edges. - The algorithm stops since all components are now connected. 5. **Calculate the Total Cost**: - The total cost of the MST is \( 1 + 2 + 3 = 6 \). This is the minimum cost to connect all components on the chip. ### Summary - **Kruskal’s algorithm** is applied by: 1. Sorting all possible connections by cost. 2. Using the union-find structure to efficiently build the MST by avoiding cycles. 3. Accumulating the total cost as edges are added until all components are connected. - The algorithm ensures the solution is both efficient (optimal time complexity due to sorting and union-find optimizations) and guarantees the minimum total connection cost. # Code Block 5 The problem involves finding the minimal number of sacks needed to carry a set of marbles with weights that are all powers of two, ensuring that the sum of weights in each sack does not exceed a maximum weight limit (`wmax`). The solution uses a **Binary Indexed Tree (BIT)** (also called a **Fenwick Tree**) to efficiently manage the marbles and fill the sacks. Let’s go through how the code works and how it applies to this problem. ### Understanding the Problem The goal is to pack all the marbles in the fewest number of sacks such that: 1. The sum of weights in each sack does not exceed `wmax`. 2. Each weight is a power of two (e.g., 1, 2, 4, 8, ...), which simplifies the packing process. ### Approach The algorithm leverages the properties of **Binary Indexed Tree (BIT)** for efficient management of the counts of marbles of each weight. Here's how the approach works: ### Step-by-Step Breakdown 1. **Count the Number of Marbles for Each Weight**: - The marbles are grouped by their weights (powers of two). The `counts` array keeps track of how many marbles there are for each possible weight (from \( 2^0 \) up to \( 2^{25} \)). - The weight of each marble is transformed into an "exponent" (e.g., \( 2^3 = 8 \) would have an exponent of 3). The `counts` array is then populated based on these exponents. 2. **Initialize the BIT (Binary Indexed Tree)**: - The **Binary Indexed Tree** is used to keep track of the counts of each type of marble efficiently. It allows for quick updates and range queries. - The BIT is initialized using the counts from the previous step. This structure helps in determining how many marbles of a specific weight are available when filling each sack. 3. **Iteratively Fill Sacks**: - The algorithm keeps filling sacks until all marbles are packed. It does this by: - Setting the `capacity` of the sack to `wmax`. - Attempting to fill the sack with the heaviest marbles first (starting from the largest available weight down to the smallest). - Using the BIT, the algorithm queries how many marbles of each weight are available and computes how many can fit in the sack without exceeding `wmax`. - The selected number of marbles is then removed from the BIT (updated), and the remaining capacity of the sack is adjusted. - This process repeats until the sack is full or all smaller weights are exhausted. 4. **Count the Number of Sacks**: - Each time a sack is filled (even if not completely), it is counted. The algorithm continues this process until all marbles are placed into sacks, ensuring the minimum number of sacks is used. 5. **Output the Result**: - Finally, the total number of sacks used is printed as the result. ### Key Concepts Explained 1. **Binary Indexed Tree (BIT)**: - A **BIT** is a data structure that efficiently supports prefix sum queries and updates. It helps in finding and updating the count of marbles for each weight (power of two) quickly. This is crucial for handling up to 10 million marbles efficiently within the constraints. - The BIT allows the algorithm to query the number of available marbles of a particular weight and adjust the counts when marbles are used in constant time. 2. **Powers of Two**: - The problem leverages the property that all marble weights are powers of two, making it easier to manage and optimize. By packing the heaviest marbles first, the algorithm maximizes the capacity utilization of each sack. 3. **Greedy Strategy**: - The algorithm follows a **greedy strategy** by always trying to fill the sack with the heaviest marbles first. This approach is efficient because it reduces the remaining capacity quickly, allowing smaller marbles to fill up the gaps. ### Example Walkthrough Let's walk through the example: ``` wmax = 14, n = 8 weights = [8, 4, 4, 4, 8, 4, 2, 1] ``` 1. **Initialize Counts**: - The `counts` array records the number of marbles for each power of two: - \( 8 \) (exponent 3): 2 marbles - \( 4 \) (exponent 2): 4 marbles - \( 2 \) (exponent 1): 1 marble - \( 1 \) (exponent 0): 1 marble 2. **Fill the Sacks**: - **First Sack**: - Capacity: 14 - Pick 1 marble of weight 8 (capacity left: 6) - Pick 1 marble of weight 4 (capacity left: 2) - Pick 1 marble of weight 2 (capacity left: 0) - Sack is full. - **Second Sack**: - Capacity: 14 - Pick 1 marble of weight 8 (capacity left: 6) - Pick 1 marble of weight 4 (capacity left: 2) - Sack is full. - **Third Sack**: - Capacity: 14 - Pick 2 marbles of weight 4 (capacity left: 6) - Pick 1 marble of weight 1 (capacity left: 5) - Sack is full. 3. **Result**: - A total of 3 sacks are used, which is the minimal number needed to carry all marbles without exceeding `wmax`. ### Summary The algorithm uses a **Binary Indexed Tree (BIT)** to efficiently manage and update the counts of marbles as it fills each sack using a **greedy approach**. By always choosing the heaviest marbles first, it ensures that the total number of sacks used is minimized, fulfilling the problem's requirements. # Code Block 6 An alternative approach to solve the problem is to use the **First-Fit Decreasing (FFD)** algorithm, which is a greedy strategy commonly used for bin packing problems. This approach is simpler and intuitive and can be quite efficient in practice for this type of problem. ### First-Fit Decreasing (FFD) Approach The **First-Fit Decreasing (FFD)** algorithm involves the following steps: 1. Sort the marbles by weight in **descending** order. 2. Place each marble into the first sack (bin) that has enough remaining capacity to hold it. 3. If no existing sack can accommodate the marble, create a new sack. ### Steps of the FFD Algorithm 1. **Sort the Marbles**: - Sort the marbles in descending order of their weights. This way, we attempt to fit the heaviest marbles first, which helps in minimizing the number of sacks. 2. **Iterate Through the Marbles**: - For each marble in the sorted list: - Find the first sack that can accommodate the marble without exceeding its capacity (`wmax`). - If a suitable sack is found, place the marble in that sack and reduce the remaining capacity of that sack. - If no suitable sack is found, create a new sack for the marble. 3. **Count the Number of Sacks**: - The number of sacks used by the end of the process is the minimal number required to pack all the marbles. ### Why This Works The **First-Fit Decreasing** approach works well because by placing the heaviest items first, you reduce the remaining capacity of each sack quickly, allowing smaller marbles to fill the gaps more efficiently. While it doesn’t always guarantee the absolute minimum number of sacks (the problem is NP-hard in general), it produces an optimal or near-optimal solution efficiently, which is sufficient for most practical purposes. # Excalidraw Data ## Text Elements 0:00 Intro 0:40 Enigma (KMP) - Overview 6:03 Family Trees (Hashmap) - Overview 10:10 Chip Design (Kruskal) - Overview 12:50 Marble Admirer (BIT + FFD) - Overview 16:23 Enigma - Implementation 21:24 Family Trees - Implementation 29:37 Chip design - Implementation 32:45 Marble Admirer - Implementation ^SvQs5Hbg ## Element Links wiqsSWrQ: [[Practical 6#Code Block]] GwfnYh4w: [[Practical 6#Code Block 2]] wqF5IiqX: [[Practical 6#example]] QP6vAZMZ: [[Practical 6#Code Block 3]] vOWC2gaJ: [[Practical 6#Code Block 4]] IEzDR8O4: [[Practical 6#Code Block 5]] qWNeF2pe: [[Practical 6#Code Block 6]] %% ## Drawing ```compressed-json N4KAkARALgngDgUwgLgAQQQDwMYEMA2AlgCYBOuA7hADTgQBuCpAzoQPYB2KqATLZMzYBXUtiRoIACyhQ4zZAHoFAc0JRJQgEYA6bGwC2CgF7N6hbEcK4OCtptbErHALRY8RMpWdx8Q1TdIEfARcZgRmBShcZQUebQAWbQBGGjoghH0EDihmbgBtcDBQMBLoeHF0ADNAhE8qflLGFnYuNABmeIbIJtZOADlOMW4kgHYRpIBWNqSeUa6IQg5iLG4I XAAGVJLIQmYAEXSoWu5KgjD5klXK5QBZAAUATjhKzGcANWd9KA4bto4AQQAQgA2XBbUqVQj4fAAZVgwVWgg84IEUFIbAA1ggAOokdTcPiFVHorFwmAIiRIy7zdF+SQccK5NBJeZsOC4bBqGDDdbrebWZQU1B8okQTDcYHaEYTebctDOHgPSXSkYADhmMtFzDRmIQAGE2Pg2KRVgBiJIIC0WlEQTQcjHKWlLA1Gk0SNHWZjswLZG0UPGSbhtTXbKQ IQjKaTcKbzMLHZnA9aqkZtNrA9XzR3COAASWITNQeQAuvNqrhMnnuBwhNCacIlgzmAXq7XRZp68QAKLBTLZAvF+ZCODEXBHYjDEaK4FJJIPHgTB6q4HzIgcDFVmv4FdsbBY8doU74c6iuBsRY5fJEsAFbYlEW3sDrK8lq8328sq+P59da+fj8P5wkm/V9P0JACeGA28322NofzAZw2kg7ZoJKENb2ceIkJKFCwBGODnGBLDfwfB58JGIicJmfDVQ o0D73Q8jbxfKDQM6T8CNoh8eDw9jMKYn8cJ4VV8KA/iQK44TPz47ZmOQ0Dl0/B5ONvHhYL/CZlO2Hh6K0p8xJYh9Jjg+dNJKGc4LaPSZIEz9JIfDpTLANCYMY6zxNvNT7KU/S5K4uD4lEtyDNvNiH3iCCfOwz8FLCxDIuI29SKkuKgt898wJClKSlkqLDJ42LHNmfysrAHKEpgnSSniaTsps+zPJCmrSrqjznKqjT4pw4N/I6mSiRyiB8FCKADX0 fQ1DHO4z19NAWy3LVCH0GtRwQO5AibEQKkPY9QzgdbmE2/sr0q9ZtDaRV5weJIl0XaYkza06OnWdN1WBB5eXnc6lR/R7JwCpI3vVdZFQmJJ1ges6HneqYHmmVTgR4YFxh+hJ51VB54gmCZVXCh5xjs7ZTviBHZwmNU2lVKYpnWJLbyJq7qpTEHZlGOcUdBnh4nR+I8eBacF2e9nzue9MeG0iZtOBoX4mB3kRYmPnifywntCmTGkyEkZ3sVpHpYmD Xk214ElaF4NpknCYZZ15W71V1TOfGMWRmN7GRlClX9eJyYca+xNQZRrnZkpmmsa5hHeYD9NwYlwPEx51VwcjhWkjaNVOfBynzptx8EmBZPU9VdPsambjI7z97U/iEYk1GBWYpVrm0x5pUrt5B4JaM47VfOrHQfWVNZalrvgTTVPpRTmWl0TWmVbz4M3Zl8ZeSR6UUb52dZiTNo53GS2IfXucwdVbfuMmTG1+nQ+t53s/94lmn4kszHp35oWMeegL VQT/vLYD2ck3ejzMGSZpSUxRtdRGQMaajFTLDecP1+qFAAL4NGKKUWAiArg1DqDaHoLQCQ2wYEwXoHABgcCGAmFMMwkw8wuEsFYEhcAACsbS7AOMEMcJwzgIAuPudAwIABKAAVVUAB5HMABpZgeoAAykgACKlQADiEicwcBGAADWYAALRtJCaEZIhQQCpOOWMOosS4mIPiNAGViS6kMRUYxhpqSilpJGRsBZ/ylDZByLkPIdJrA4IKCoATxTyhnH jbQb1qoxNicTWU3AFTYzOinNMCde5Yz5mYkk+pDTGjNFaS0SB5h2l3FmIQzo8lunQB6DgXpcA+igH6AM0YkjJBnB0zpM4kz10gJIcMkYmloCuu0rpXSaaxgQPGVAb19aThHjPSA5Tcz5nyANMsFY+FzTrBU4gHiNytlDO2XZ3YMhZAvGgAcoohwjk4cyScSpqGqTVM5Qaix1yzU3NuXc0zto8JPNNC5hZ3KEzglZWqIK7zGW0GLWFcLYWjFGI5BU ML4VosRdnMAcQ0XovGK5CFwVtgYQSHE0lHQGpaVRTihFeLkWJFJWSx+/kSUMtiWTOlLLWWPyZaBKl1LZi0s6uxbF/KxYYuhaKsVgrUq5TpvhEVkrKZNTKjhelXLqpky8USyUfNdV6r1YqRyM5RljI6T0kSExVYZOtb3R+RqRhSjGE651Yxj7yrVeqzG+LmqQqxTq/VAa3a2JKAhNpprxlLkcqmM6qZY1xtTIjfCMtHLVU5aysm+EpgxvjTm71Kqp L+oDXqt2mbLU2vLcqlq2xiZRKLcW92RKs05ubXmqtVUPVcozbxQtda+YlXzWFBV/K5i8XBT6wlVVKrwWmCa8NPSo1lvLRkrJwqx0Do8m0bNzaE29JDT23thqhX2TDeGs1S44L7rrYemV5UShCW0F/R9T7H0MzgqMR1LrP2TEKpewN4VjKqgfc+4DDMf21t7Ujf9n5gxWqXbasDEG+ZBosouuDodnz9RXMNUa40ZC1CmueA581QysCWkNI4a1GSbS 4Uef5u19qHUvHKrusa8bhRmJPZ62MUbBhmGMDGQbsbhQpTnRMaoUxI0LusFMeN97cQlg8vOj8ZgpzXvrDUS4ZxI3emMNTWsoagyxqmRmInTqKzHgnSm1dNME1trqjoCNQZKmfldC+6YOhTHOkkFzWrRNI1TAnJmUwR4AwvimCm0me6j1C8PfzEWgvRd3WZvtxN5yzhdm9XzZnHYLmNgDKuAXFmibxVXOcQk8aFbXkzISwNLZN3WN5qr50avzkfsb BrDbivNYTq1+rjXh6TjS6l2G7X+t03Aw5tLzmFaueHtvZrTnLLw0LuzZMN11hcxnJTK6pmEjJmelrJGc9uKY0649b2ZWtbeaBtdBBfUSgoMKGgyAGDHHVCmTg+YeDODDEWUQ5o/RBgVFnGmGmLc6HLDCegXA64Lj7EOL87hvDVgAEdKjxGwJgHM+hRH/GIAATUsJGB4nY+iAgQPEPRUJYTwkcSYm02ocmWOsbwbJ9jaeImcaY1xwh3GMmGKydknJ YD+P5EEoUoTEkzhncDT9zrbMQDlKgBU10UmpijtaldJHzG5NdAUop1oSn2nKZUvX7pyB1O9Oc5pVjAxoF/fqwz8x+kRijMyVDaGeUkY+9wauqZ5nb0zLSFZ/Z1nkE2URnZDZ+efMOaUY5SxTm9iBVc0MNyVp8NGFOJ5BXbFvLXJH0URofl8L+fMU854joTsfGC5FQ7qXis/Gutt8F684sb+BPl/KlV167+3vFSa03ptbb6lFkqBUjoAg7/V16CVp SJW33Fk/0KAeAyB7zjkp3OFX2vp9oGj3MYAsa09Z7CIH8pePjv6Fj8n6Pmfm9gk+9L8xYBTd2743jBTUPxlIn4Ipy3e/p/uflVN/nEuSharBp7v2i3h2qyuAexP/u/nGkAQ/lJKAbEvAUfieifvOsAWALAQypgSvkBrvl/PvqgVxE/vClftquBhBrPuOvPnelQXCjQSGgQQyuyngYvtQQPrxOgWynmphkXthgYLhpNICoXiRotMtBRgxoEDRjtKU HtFRutExqCl3N5jVldIXDLJbPjCjEHDVtMDDFdG9GzF3NxIXCPPrDvMGIqInCxtJl/DMO3FHFDBHCxnnKMOjDMGtv3HnDxlHAjJnOqObGNirGmO3BxumGTMbNMLtqnJzFpouGDCDGDBDKnBTMTMDOFG7NYY4eNqnI5m9BbKDB5rZjnNkeDI/F9LvA8n/EuLyCmAnFzEZlXAHJvOFNpNvP3LOLoZ0V/DTBtpBtTP3AHDLLDFDCEQsmKoYVzCZl/AF ODKLGdogg9qgqKK9lgh9uQPUKKN9q0KgBSv9iQmQhQqgCnGLNymmIQosJDqsAQKwvDhwojrRsjhIJgAABIcDyJfHMA3DVTEDYgk4yL0CkB7B6hQAABiJopY1ODinOyIbOFiLSNiKJCAiJlIXONobi9IMelxguviIuzIvIYuwS3Aku8ovh2gsucu/GCS8oySCRaS0cvcWupQjOuoLo+SEg5oBuxSbYxuToxAPJ1S0AFu9SjSNuLOp0csWqYYruQyw otJvICpcYfCSxwcCcCpyyeYoepY4eCAlYsexGpQIp+yppJSHYSe5yBp1yw4GeE42ePRiMFhoYq4HyqA2yReO4e4ihdGyhgKVeTBNeTem+qpDWYKkZgU2UwhHpohY0E0+GkhVpC0ZGK0lGG0ChaAaIQggZkAKh2Z4QayyCmxoY2xEgGQmgtQI4mgCIX2xC+CpJZJBxTZgO5CFQFm0oGMCp9xDC0O+gzx7CCAdyqAZeoolwEg/oKOzAMI2IpA8iVOB iHOEgkgHIGgChGJzOduhJWoOuWJ6A9OOyfOTYAuooPiwuSuwC5JEu8wUOgEkwlqVckqjJyuZ82gi4vIHQg2NKGJYpZocscsNopSDoIpAF5unoVuvo8w/otu0Yz0dBvaJxLugyQYMscQI8pByYky0yKm8SooepqylyYe5YxpWyXyPOuylp3plFRyNpPYdpayg4jpY5WejyK8AsJxnpqweQeQeO9A1gYgxAqA/w+Aygxoagkg+gEQa0HIUA5gBAqAl q+gxApoBoywqAgIxeGIRYRYNoOlbxShkA2AQg2oBgewo4YIaAz2kAqla0bIBYtlkAZlCAoi9gJATgBwpwNYQKE5D4towpHYNwo42Akgeo1gglBY/lt4gVZSIpIVUAYVtpM0qAeZBZ8eQVuyEF6ApolQ+VKImV8VHYoiQufi9uU6toxoywpAiVyVjFqV6VcEVVpANVOVEAeVBVzV7YrVTApVxJSulV+iWQZFbwuwhA9ZW03CV4SCRIj2JG7gFQKEv msZRYZZT2Wx5Qqw1ZtZuAk1uC7ZRxXMAShx5xwOJcfM2M04EOA5gSw5COpeSOk5fCEASiFAlQHA+Okg8Q+xoY+iNO5Iji65YV1G25aJe52uOSh5TiyJVFp5niRJV5ouooAod5ooUOcpx8261MgeooSuKucQKo6o84QFEy+5OS7VpopNmwRuxV2VVSqwtSUp1usF4N1cPB8KzuAybuvAkxqsWN2NuFmeWcCuRF9pf1RpJptFcekAFpBJPp9FJyDVK eA06ebFimAMzsZMFMK47yvF/FxAglnZIlYlElpAUlMlCgcl2ACl7gyl2gql6lbAml2lfpvAelBlfpRlBZEApl5l+gllUQ3AtlEA9l6Icgwdn4EArl7lDgXlCAPl+AflT1AVoFJuxAdV4VkVoQAZ3VWVSwmdKVypTVUdad4FDNfJ+VlQhVkAZdJVZVJJMylVPVNVhdyt3AJdqd1VTAlNVdNdLVNV/VSNaAQ1UII1mQY1rA+1B401t4s12w81nJi16 hZkP4q161JQz2ZQmCVZ+gNZxAdZDZbZAOR1xsjZJ9Z1wwEsx81cr8k59CUOawbA91rxj17xz1qwFAKO0JEwOYhAKOGiy5ANRiwNm5gpkNuoO555EDpIq5R5OJJ5+JZ5zIiN5VlxrZoYqNIS95iS042K9JYw1Ub5KcbS6Yfas4sS84eeXJWIlN1NIF+dopFdNSkp0FTSrN8FaAoMDqBDTqvSipaFNi5hUokq/DGpBCX8Qe2Y+pzFooGy5FUh5pHYN FCt8eDFZyfYsjaerFeFGtR2yYiYutBeEgfFAlQltQol4lkl6glt1tttSlKlalWA5YPgCAHt3y/pM979oYftUAFlVlkdsVYdjlgTsVMdHljg1g3luAvl0VKdsVdduymdEVHAUVudpdjDbdGjxdpA+ZeddNpuvJuVfd+TYF9dA1Eozd3dtVoVkgRdHduTGVtd1TvdXVpd1TQ9aDo9wQHAo1410945s9C9c1sYy9lyV4K16xYAi9W9lZVQ2CexB1J93 ADapxLQl9XD2kCMVDdxD9qwmg6wMiL9o53tHx/CXxzgNwNwGi2IbwHAbwnYmgpAAi0JMiQgxsMAAiQD0Nx55NkD4N1DB5cDMNLioYeJNFCpl5aDN5KN4u2D6NiSCcn5MsVcl8G2g2Cu+NkwcQj8yRCsbsccryNDuuRTHVhShuQpBTTDZuLDUFDSLNoocFLOHNNKy+fS3Nyp0+Bqf24jzIE85hFcUjQ4MjJFhpZFUtqjstyj8tdFajSt2T4tpQatu jLpioKYQ8HpetaZHpXtb9xlEAFemj4z1eU6zeo+LLUqbLJQXLuqDB66C+LBrLmKNrfMdr8ZpQQ02oOGyZxABGqVkrxiMh5Gq08hU13jyhobIZUKx0d2cZGxG1FZW1Eg72tQiz59JC3Af2p1QOmbC8lMESN1j9BzNwxzY5MVOwL12IxM+OjAQi2I2A2ipAdwMIOYpA+Oew+OHAwI0J3zwLvzMDOIALGJPzCDcNSDCNF5Dd15GDpQWDlJODmzkMQkm q4UTRnhoY+NiMlqWtbs700msMao/5zDZLApDDVL7VTNbDMpu5FrE+hCqFPNN+OB56WoPuaAhcC41cZMhCYtWjEIktFFMtEActyD0tZpzT8ryeirLlOjmeGtYsX5XamrxjYHHjpzAKleK9t6YZD4ZrJr8qjrcKlMm+teeBU6t7GKJHaBnq1Uvel+fBD4Lr4cdHkqbBYAO+2FVmLHoqbHHH2FXB92c+kAnrI0YhPrfrypAbpGshIbqhYb+rRZB0ahx rGh42yRkwwYhcsMKxXMlRj0vIr088Kc37pcLGFMWtUwow5MwYSHkRf0KRQM6RhRkRx8NMioUMmMx8qmLG10zsNMeDvZyGx0UzMzm1O98zuxlASzGb7Qqz2bnZwwgcUMqkznOwezEgBzfQpb6HoYU5VQmAoiDwMI2iAiGIgIqoGIzgmgdw2A9Az0gE/wvbgNSJoLnJOuUD6JfzsDzX2JsNYLvO470D3iU7yNmDcL87CLaA0okoN9lk3s6rMYeNiSl kDqi4Wsjsc3iYG2R7NLJ7hSZ7ZT9Nu3l79LMFjL4NMGaGmS/DD7nLHBpKYjb7xxiohc/8ryv7orcjAHijUr1FMrQHCeXYyt0H0dsHzpjyCH7cGrHrWrqHvpJe6Tu0wZWHOEprVHne4+YsxHeB93GBXu6Et7WPvUQn2HfHa+DMwaX4eBiBSBHQYCvKmPy7hUFHzrSFV63kFBKkuPMSRB2wZPu+AnJPgk3P1UvPZk2Bp6uBnPWkLreMb6HucGdq3Bs vmK76vDzqhUIvNxxkhP0BvqYshHAqxkWvla+v5HTHev1ekwkBS6ecmK96nHX8gvjBsq2wNPSBbRAGJB/HI+VvCv5advXvjvXH1PPD6vbqoE/Pa+zv9rd6LPFkEvc6ka3BUfe+ERMEb+tP0wpv1eM6t+JDv+qcH66vUamfHv9Pg6hvqkC6NvAfu6+BJvpfABzanvn4oMs6pqUvQv0GifpqCRcEWMtfFahUqfL66fqEYf6vvvoZnMAhov+P2wiYhUU 6CMVflvoZav4fv+q/mP6/rvJQo/ZBWhF6uvX+NHYvYAS/3BhvbHV/0v0bD4ZMxfvD6Pt4T/U/YwGGMqg0iZ4hKZhG2rTkkG0zKhtEeEbOTlGxw4Nx+4vhFYmMDjhIwIYxcPduZmZiSMu4C4aYAsiRhXEFYOMdmAZi0xadkwswaYGvAaxKgcY0mEeG5227Dxci4wATHzChhadyBDsChs7HMKsD6B12BrIjAixzg6B42LbvrGDBYUE4ggs7KrEIEAx iBk4EhgQLEw4xQ4m2fWEln5pLF8WwMe2GmCyz81OY1hYMGxiWLswtYUwZ2JZBrijB0wBArGO9BnCYxYY+sBcAQNaLuY5wSRBQfQPBg/xxBtAqQYmHSxUCLB/g8gTzCEhIxVIKcN6NDzsyRZM4WRBTApnIEtFPMKYJIaZ2EHPQXC63BIjEODRmZsh6oXIdEPegFCokNRAGO3DEFRFYhomfuNVHzYNYuBiocgbwJmBYVQh3goCqpBxjXQyYRWU6AuG iKyCXu8gsgRgKVB1wJMgMSYILAwFRFjY7cRUF6jc6dFo4FMOcNqVAQPQQu5ZdBEm3QA7UD6e1I+qGEOIEh8Bx9M4jm3tyiwfCCcQtvs3WCiJsuerH2nlwgDyI7gwIegP8G0Q3BdE8JFcj13QCgNQaXXQdpwwhptcoafbUdv1zpAQtUGjdGFmNwpIj0F2qAD9iIzRaosKGb5BUEXzW54wZgm3ZoTt1JZU1SaB3dOhe1Yand2G53aEdEQ75jJVmt3Y YHHDn5Esnu8cEtIRWDwitCwpFCPIAN+7R5QOAbQHkXRB7Ks4OLpfzCPH4Y8UTGBtI2sJUsZm0LaslcgDbUUr4B7ajtDSggC0o6Vji7jeHp40GbhsTKZlPxgHQCY2UrwodX1uHScpR1wmcdKJgnRiZJ04mtooqodwLq1MUmaTLxsZSDHp0smyeBpnkwybntj2nVauqU3TqdNG6hjdpr1RqZJU6m7dXMo026otMkxJTLMYPRG6Yio6w1XphPX6ZCg/ kM1EZlqDGbAp3wa9PYQmwOHhcMAe9XavtXTbNljiXMAcR2QuKacAYPSE4v2SLbrA7gbwsARW1WD0BRE2IPUDwGUC4AAAUk1xAYbkIRA7DrjCLsTdcjE/bJRkiIJKQsKx6DAJHO0rGhgoc/QuICfCsxaEmiryTdrPyUwjw1QlArdmIx1x0MaRtNYMdS1JYndpSHDFnF5zZFdJXknImxJjDL7v4havuKuOZEFHSNiKIosVmKLh6IjJRzYWVhB0TzA8 /2MHW5Cqwh56FiYf2VUegFMaG1zGJtKxubRsa6j5KBoo0WpRNFmi3a8QS0TqwR4RifavjfxkHWdFBM3RITSSWEzCCx1PKPoxOsnUDG11MmoY7OgGMjFqSqWMY63AWPjGp1GGrTFMQmNAnpilcmYrutmL0mNVCxZYnuiWLabWTyxFTe8bFWrF9Mp69YoZg9ibELUCAS1CZu2ME6hdE23Y44YfXAaNBDqCFQhPFwuItxNhHnJ4Rl3WBLk4cI5MtvE0 XESAcwnYIwHsAERiJKcwI4BkDT3FblIRh49UkC1BEgtuciI+GkN0gBQtURM7SAHeOFBYjmYS7V8au2upLd5QYsGbs7CaIRINsS4QFhTSTH0MQJdI49hBIZahgmWu5AzEP2tR/YEJvAMad73J6Pdpka3GWEKxDzkSIA8jCVsROA7SspRV0mUWRM+7aNKJCoiHmoJnB556JEARiRqIsam1rG0lDifqLtqOMnaLtc0RMEEketdWC432vaPEnWVUAIdY JhHVklp55JETeOspK0lNM4qoE5JppIXFRiEqtTepgZJxmJNCm4pZMf3QpnEALJlTIsTZJJn5i0q9klyY5N27UzGZrk4et1KrFj0axCASehNR8m0ZGxC9UZoFKw6TNQp+wl7IcPOkLMouI4o6idUOobNsR6MdbtDFSnoADmXzTKQ9RhmfCAAqvoBuBvBtEewURH0FWgmztEnYVQDAGcCbiYAFAEtmVJHZ9dYR/zaEdNPZz1Szxv3ZqSg0nZuSbxt5 eFg+IJAeDVY/cP8TDEnCEjt4o0shg1n/htEAJM0zmeS2ik6TQJ9IulpBOZHQSAk20wuGv1Ql8sVMUMcGD+yFHYTU8/7cVoB3A7XS/ut0gHuoyg5nT5R4PPBuEMD5F5YeAbQyu8PLzI8VO+/KAYTFf4X5Me2Pe/q3kI6382eRaZPkvLHw8cGO74f3hWj37Yct5w6HeW70n4l88CxKc/gvxKCXcrulsDlFfML5nyX+F8rXhf3Chr8H5nqd+Uxw3nd8 AIb86+U5F76moc+oZS+d/KAXv8P+X89VBfx37j4D5OEI+Q3hPnWsmOHPf+QTxXloLcIIC9kdx1QVWtcIyEwAoQv7zEKxgz/AhuQufxwQE4tC3gsQorm7855B/U/tf3HwIwz6tkDhUvMAW/4WFiCxyIf3Ri5Z6FeHDfqQubQYwJF9qahS6kLgR8HwVCj/tP2nkG8uFiaT8KopgUp89pIGcRTor3k2owF082+Whg5LbBpQG061GYuw5F9w+QkOCDYr vn3ycet7bhS4pMV2KU0KvbxbYoyT2LVUoi9uN9GMWBL4MeBQfnfKsUlBXFd84JdFCnQJKruSSxjoAoCVuL0lt4dMAYufThKVFPioJY5HGCwTOkEwopZEvQx4EUwzfHNLIoiXZLSlxS67lksSUtLqlcS3CK0pqWCcXeP/L1mJzwy+tUy+EoARmTkJyc4xPtRToxhIob0igYXN7ErN+oxTlmNieKerNuGXFtsK3D9rrNtDrAYQ84kSWcwgAYQJEJsz sOoiYSqgOAFAOcjcAERtAMQXxHgCjgylyMES8I72ceKhHMth2vy1rsHMG6hzQwbU6drePG7uTIAUOF7pulEEJzt4ScoacrlTgPBaStcwuB0D4xJhKRVM3ObSPLrHcGRxclaeDSfZzoUWXNJUsMCpWnpVmvLXZW3ACiTh65WEkHhdNblR49k/3NufdIVa9ywe9yF0jzEsjtwjGXpUedDLOUYcjWrY0MmjzI7MoaOXqNhWAAZXholem8i1mx2t6xLG FrBXBbPzVUx8W8BENefqnmESR8lBSjfBfJdY2rWoXS5FKIqXA4UpIki6edvjtVPp0wmKLmH6rH45KiU7qgNQP3wVdJQ1IacNZ6sf69L78WCsNcGq/gRq2+z8mhRfLjXOs+Fya2Namo9XOt3VV0N1YWvTUqLpFLaZFFqvGQ0qdFma+kjWqjXdJ61D4IRZKiQUIEW1YMNtbeCXBWr9UmCl3ofNrWdINsQgxKD2ocHNrylrayddsHbiuqL5Y6s1H2rd 68hZ1t+CdZ1k1UbY5+1UJNSOuQWrr51u6sGBgq3U4F11ZkJMIWtLUrqe1O6t9Keo6Qxq/8T6m9ZqtfUzqmI7rYTr/3E5jKpOwAqZcWRmXl5I2pZeNpvWWXbVexJw/sdcMHGxoVZGssGKmAVjXxDlBzIRKcptH6tPhKObELbOhI8BMEns4FuCKqkHjKVQKwOQiPPEhyjxEASFaN1nYwq+Z0ctABhTaRCRRgORc2I3EJECpPyhsDbg1k/YErAKwEyl gXMWlkrlppQVadGBiVXcFc207zHUqz68jpk2MSgSdOFFNzIA3Kn7u3MIlmbBVPcx6UqxFWXF4OiYXrFKv1pmNjaWo/6bYz1H2NDRIM3ia7V3AzJIZwnWVQRtElwzHREkxGS6ORkeiAqXoxSRwGiaxNCZ+c6MRpNSY505VRk3SczOyYQazJC0zmaWOy3mTrxVkhJtU1sk5NDJFW7MSZJpkdNrx3TcekLLrHycEA4smDUvSllTzNVIUuNtMzlnb1HE RwTAEyPOGxSeNqcNDTsow2Jhy4yi0oNOOeGANDZr9Y2S9RhD0B5EzACYF8U0DKAdxdORjf8sPH+yTxx2v5e3OY1Xjw5aIjjRiK42lAocnGZIMUUkwGNZkxDS2MqC8yrsV2t2SEZTSJXzSSV4ExTWdwpUsjLu12EwrjBei0rBGqAWiVEmTDNwPCGOquZcRmwBQ1WhmxuaKIUbijzNfKzuQKu7lMUbNFEp0qKseTfwBU3FEeVdLHkwzKgnAKADCEIB GAKgAUKUOjox0Y6X2f1dndCXLBQglcryUbcqQgDrBkAvIVAGoh1AAAdDgLLplioAblEYfQLgFQAAAKCRPcAACUyuVAKIiaBmAEAFAFXcCDl1tBUAou8aPgBgCoAhENQZgHrq+KhBpKuAOAMbucCm7zdhAS3SrrBjIAwYqAPUJIEIBwBUABwVgMoA4B66JEuTZgBiAIB+6A9TAC3Vbo4AzBkA+sVACFVICTVRKxAcaIEFIB67AQOYIRKgAADU9u6E nsAz1m6s9QenPQDGQCqQNdHALXTrv93Y5XGyeUcC0BV2zAu98Qe3WLqd0u63dJuwfcrRH2cAx9DwZAKnAj1R6Y9ywePYnoH36Ah95yJfRwBV3nRkAmMQvQ0hL145y9TAeffvsX0KUfsNISgEIiwDS7Zd8uxXeiBV1q71gPevvUnqN0m7W9pAbPdbtt1T7Hdzu13VMnd267PdzAb3b7uAOB7g9ue2XeHsj3R7Y94QCMInv10p609+AFvagY708B89 f+ovVfrL2EAK9VemvfXsb3N6UDbetA53u72a7lA2uu/QfuyBH6x9SQCfZAfF2z7YDPBh/aPo4CKg19IwDfdge314HxD2TfgxwFP3n6qDwQUvTfsr177eDUQR/VwHmBS6xKeB1YMEEqDjb1l3mkwwnsZpsgbQegPg4sCYCXSgOklfwAQFf1jbVgH+v/V/rYA/6z9f+zg9wf11AH/dIBsA12wgMO6RDMB8IB7q93a7kDER0gyHowN/6sDMeuPYoYIN mUiDJB1g2QYoMX7i9mh6/bQdv267q9tehvdCSb2FHQD7ekPTbo4O96uD/ehXffuUMGGBDQh2IzPviPu7dDEh5fVIdX3r6sjqABQwnqUPD7ejqh8g+ocv3lGaDdBkYz0fwT8ghAfjARLge535bkOCAL4hy2GAwpeo89ZiGsDgBwA4QK0UJtAH6SZBVgI4UgLDkKAMB29gIYyU5KrqmTSgplUgI0hzBHB9AcIbOVSJB0fHATwJ0E98cTGkqi5Sm2GU CfOQgmMg0JH5Qxu9konYTGQcE77MBXQmRAeJsE3VNPGMbcTaJ0EwIgG7IjiTqJ7IOiZxzXj7tVJpk6CehIi7p9EuhoOyagDMmuT2QTnQcZsT0R+TzJrw1ABsNHFBoCdSwxKdBN3HSA0poE2wAoD9JcAPKhk6SZuXEB/gapjUyEBeoNJ0QayxUxkANNmmhECskUuCGMTYB0Q0IQBvKFgQOpj42kEHCPDZXKwHTTp/APjilwYbVY5cNrD4JFh8mjAb AAwKE26AEB8ylJKJBcb5MwnqTGQWkx3ILAk77TjoEgCKYqDaQ+TuZ4gHCAQBwBowRZ82hnWdoIAblpwnLksirM5Ut6gIQ0C9XUoPA9QnZh4DaD2PKBloVMzsHsCHNDmIAiy/kwSaxClUDDREuPIrLIp7H6E5tIJLGYwC9NJqfCTugCaIDlmyZ8wQWYce8Q7HVwFQdKuObsBMIEANtOcr0zgA3AazdZjc2ALWD6ja20Z/AKubmbGJ0g+op/aKDEn6 AbT4XGVcJNC1YZtQ/wV8wgCETvmiMyCcAPNXOlj0nKs1JBEAA=== ``` %%