This commit is contained in:
Nathan Wang 2020-07-18 20:26:46 -07:00
commit fb8a38e3ed
29 changed files with 702 additions and 176 deletions

View file

@ -16,6 +16,7 @@ export const metadata = {
easy: [
new Problem("CSES", "Distinct Numbers", "1621", "Easy"),
new Problem("CSES", "Stick Lengths", "1074", "Normal", false, [], "Spoiler: Optimal length is median"),
new Problem("Silver", "Teleportation", "812", "Very Hard", false, [], ""),
],
}
};

View file

@ -16,12 +16,14 @@ export const metadata = {
new Problem("CF", "Div 2 C - Maximum Median", "contest/1201/problem/C", "Easy", false, [], ""),
],
usaco: [
new Problem("Silver", "Moo Buzz", "966", "Easy", false, [], "binary search not required"),
new Problem("Silver", "Moo Buzz", "966", "Very Easy", false, [], "binary search not required"),
new Problem("Silver", "Cow Dance Show", "690", "Easy", false, [], "binary search on $K$ and simulate"),
new Problem("Silver", "Convention", "858", "Easy", false, [], "determine whether $M$ buses suffice if every cow waits at most $T$ minutes, use a greedy strategy (applies to next two problems as well)"),
new Problem("Silver", "Angry Cows", "594", "Easy", false, [], "check in $O(N)$ how many haybales you can destroy with fixed radius $R$"),
new Problem("Silver", "Social Distancing", "1038", "Normal", false, [], "check in $O(N+M)$ how many cows you can place with distance $D$"),
new Problem("Gold", "High Card Low Card", "573", Normal, false, [], ""),
new Problem("Silver", "Loan Repayment", "991", "Hard", false, [], "requires some rather tricky analysis to speed up naive $O(N\log N)$ solution"),
new Problem("Gold", "Angry Cows", "597", "Very Hard", false, [], ""),
],
general: [
new Problem("CSES", "Factory Machines", "1620", "Easy", false, [], "binary search on time and calculate the total products for this time"),

View file

@ -13,7 +13,7 @@ import { Problem } from "../models";
export const metadata = {
problems: {
sample: [
new Problem("CSES", "Building Roads", "1666", "Intro|Very Easy", false, ["DFS"]),
new Problem("CSES", "Building Roads", "1666", "Intro|Easy", false, ["DFS"]),
],
general: [
new Problem("CF", "Bear & Friendship", "problemset/problem/771/A", "Easy", false, ["DFS"]),
@ -27,6 +27,7 @@ export const metadata = {
new Problem("Silver", "Milk Pails", "620", "Normal", false, ["DFS"]),
new Problem("Silver", "Wormhole Sort", "992", "Normal", false, ["DFS", "Binary Search"]),
new Problem("Silver", "Moo Particle", "1040", "Normal", false, ["Sorting"]),
new Problem("Gold", "Moocast", "669", "Normal", false, [], ""),
],
tree: [
new Problem("CSES", "Subordinates", "1674", "Very Easy", false, ["Tree", "DFS"]),
@ -38,6 +39,7 @@ export const metadata = {
new Problem("POI", "Hotels", "https://szkopul.edu.pl/problemset/problem/gDw3iFkeVm7ZA3j_16-XR7jI/site/?key=statement", "Normal", false, ["Tree", "DFS"]),
new Problem("HE", "Birthday Gifts", "https://www.hackerearth.com/practice/math/combinatorics/inclusion-exclusion/practice-problems/algorithm/mancunian-and-birthday-gifts-d44faa15/description/", "Normal", false, ["Tree", "PIE"], ""),
new Problem("CSA", "Tree Construction", "contest/860/problem/D", "Hard", false, ["Tree", "DFS"], "several cases"),
new Problem("Gold", "Cow At Large", "790", "Hard", false, [], ""),
],
bipsample: [
new Problem("CSES", "Building Teams", "1668", "Easy", false, ["Bipartite"]),
@ -45,6 +47,7 @@ export const metadata = {
bip: [
new Problem("CF", "Bipartiteness", "contest/862/problem/B", "Easy", false, ["Bipartite"]),
new Problem("Silver", "The Great Revegetation", "920", "Easy", false, ["Bipartite"]),
new Problem("Silver", "Clock Tree", "1016", "Hard", false, []),
],
}
};
@ -67,7 +70,59 @@ export const metadata = {
### Implementation
(code?)
Iterate through each node. If it has not been visited, visit it and all other nodes in its component. The number of times we perform the visiting operation is the number of connected components.
<LanguageSection>
<CPPSection>
```cpp
//UNTESTED
bool visited[MN];
void dfs(int node)
{
visited[node] = true;
for(int u:adj_list[node])
if(!visited[u])
dfs(u);
}
int count_components()
{
int count=0;
for(int i=1;i<=N;++i)
if(!visited[i])
dfs(i), ++count;
return count;
}
```
</CPPSection>
<JavaSection>
```java
//UNTESTED
boolean[] visited = new boolean[MN];
public static void dfs(int node)
{
visited[node] = true;
for(int u:adj_list[node])
if(!visited[u])
dfs(u);
}
public static int count_components()
{
int count=0;
for(int i=1;i<=N;++i)
if(!visited[i])
dfs(i), ++count;
return count;
}
```
</JavaSection>
</LanguageSection>
<IncompleteSection />
@ -79,7 +134,59 @@ export const metadata = {
### Implementation
(code?)
Trees are generally treated very differently from general graph problems.
Typically, after arbitrarily rooting a tree, some interesting pieces of information are a node's parent, subtree size, and depth.
The implementation below computes those numbers.
<LanguageSection>
<CPPSection>
```cpp
//untested
int parent[MN];
int subtree_size[MN];
int depth[MN];
void dfs(int node)
{
subtree_size[node] = 1;
for(auto u:adj_list[node])
if(u != parent[node])
{
parent[u] = node;
depth[u] = depth[node] + 1;
dfs(u);
subtree_size[node] += subtree_size[u];
}
}
```
</CPPSection>
<JavaSection>
```java
//untested
int[] parent = new int[MN];
int[] subtree_size = new int[MN];
int[] depth = new int[MN];
public static void dfs(int node)
{
subtree_size[node] = 1;
for(auto u:adj_list[node])
if(u != parent[node])
{
parent[u] = node;
depth[u] = depth[node] + 1;
dfs(u);
subtree_size[node] += subtree_size[u];
}
}
```
</JavaSection>
</LanguageSection>
<IncompleteSection />
@ -113,7 +220,6 @@ The idea is that we can arbitrarily label a node and then run DFS. Every time we
```cpp
//UNTESTED
bool is_bipartite = true;
void dfs(int node)
{
@ -134,6 +240,30 @@ void dfs(int node)
</CPPSection>
<JavaSection>
```java
//UNTESTED
boolean is_bipartite = true;
public static void dfs(int node)
{
visited[node] = true;
for(int u:adj_list[node])
if(visited[u])
{
if(color[u] == color[node])
is_bipartite = false;
}
else
{
color[u] = !color[node];
dfs(u);
}
}
```
</JavaSection>
</LanguageSection>
### Problems

View file

@ -1,6 +1,6 @@
---
id: greedy
title: "Greedy Algorithms"
title: "Greedy Algorithms with Sorting"
author: Darren Yao
prerequisites:
- intro-greedy
@ -27,8 +27,9 @@ export const metadata = {
new Problem("CSES", "Stick Division", "1161", "Hard", false, [], ""),
],
usaco: [
new Problem("Silver", "Paired Up", "738", "Easy", false, ["2P", "Sorting"]),
new Problem("Silver", "Lemonade Line", "835", "?", false, [], ""),
new Problem("Silver", "Why Did the Cow Cross the Road", "714", "?", false, [], "first step: sort!"),
new Problem("Silver", "Why Did ... (helpcross)", "714", "?", false, [], "first step: sort!"),
new Problem("Silver", "Berry Picking", "990", "?", false, [], ""),
new Problem("Silver", "Rest Stops", "810", "?", false, [], ""),
new Problem("Silver", "High Card Wins", "571", "?", false, [], ""),

View file

@ -24,6 +24,7 @@ export const metadata = {
new Problem("Old Bronze", "Haybale Stacking", "104", "Easy", false, [], "**Task:** Given an array of size $N$, do the following operation $Q$ times: add $X$ to the values between $i$ and $j$. Afterwards, print the final array. **Solution:** Consider the array formed by $a_i-a_{i-1}$. When processing a range addition, only two values in this difference array change! At the end, we can recover the original array using prefix sums. (The math is left as an exercise to the reader.)"),
new Problem("CSES", "Subarray Sums II", "1661", "Easy", false, [], "Find prefix sums, then enumerate the right end of the interval, while maintaining a multiset of all prefix sum values that currently exist to the left"),
new Problem("CSES", "Subarray Divisibility", "1662", "Easy", false, [], "Find prefix sums modulo n, then find number of pairs of equal prefix sums"),
new Problem("Gold", "Help Yourself", "1018", "Very Hard", false, [], ""),
],
maxsum: [
new Problem("CSES", "Max Subarray Sum", "1643", "Easy", false, ["Prefix Sums"], "Enumerate the right endpoint while maintaining the lowest prefix sum that currently exists to the left"),

View file

@ -25,7 +25,7 @@ export const metadata = {
new Problem("CSES", "Sliding Cost", "1077", "Hard", false, [], ""),
new Problem("CSES", "Max Subarray Sum II", "1644", "Normal", false, [], ""),
new Problem("Silver", "Diamond Collector", "643", "Easy", false, ["2P", "Sorting"], ""),
new Problem("Silver", "Paired Up", "738", "Normal", false, ["2P", "Sorting"]),
new Problem("Silver", "Sleepy Cow Herding", "918", "Normal", false, ["2P", "Sorting"], ""),
new Problem("Gold", "Haybale Feast", "767", "Normal", false, ["Set", "Sliding Window"]),
new Problem("CF", "Books", "problemset/problem/279/B", "Normal", false, []),
new Problem("CF", "Cellular Network", "problemset/problem/702/C", "Normal", false, []),

View file

@ -21,6 +21,8 @@ export const metadata = {
new Problem("Silver", "Rental Service", "787", "Easy", false, [], ""),
new Problem("Silver", "Mountains", "896", "Easy", false, [], ""),
new Problem("Silver", "Mooyo Mooyo", "860", "Easy", false, [], "Not a sorting problem, but you can use sorting to simulate gravity. - Write a custom comparator which puts zeroes at the front and use `stable_sort` to keep the relative order of other elements the same."),
new Problem("Gold", "Splitting the Field", "645", "Normal", false, [], ""),
new Problem("Silver", "Triangles", "1015", "Hard", false, [], ""),
new Problem("Silver", "Meetings", "967", "Very Hard", false, [], ""),
],
}

View file

@ -17,7 +17,9 @@ export const metadata = {
general: [
new Problem("LC", "Max Histogram Area", "largest-rectangle-in-histogram", "Normal", false, [],""),
new Problem("Old Gold", "Concurrently Balanced Strings", "194", "Normal", false, [],""),
new Problem("YS","Persistent Queue","persistent_queue","Normal",false,["DFS"],"")
new Problem("YS","Persistent Queue","persistent_queue","Normal",false,["DFS"],""),
new Problem("Gold", "Modern Art 2","743", "Hard", false, [], ""),
new Problem("Gold", "Dishwashing","922", "Hard", false, [], ""),
],
}
};

View file

@ -3,7 +3,7 @@ id: bfs
title: "Breadth First Search (BFS)"
author: Benjamin Qi, Michael Cao
prerequisites:
- Silver - Depth First Search
- dfs
description: "Traversing a graph in a way such that vertices closer to the starting vertex are processed first."
frequency: 2
---
@ -20,6 +20,7 @@ export const metadata = {
new Problem("CSA", "BFS-DFS", "bfs-dfs", "Normal", false, ["BFS", "DFS"]),
new Problem("Gold", "Lasers", "671", "Normal", false, ["BFS"]),
new Problem("Gold", "Dream", "575", "Hard", false, ["BFS"]),
new Problem("Gold", "A Pie for a Pie", "765", "Very Hard", false, [], ""),
],
example: [
new Problem("Gold", "Cow Navigation", "695", "Normal", false, ["BFS"]),
@ -33,7 +34,7 @@ export const metadata = {
<Resources>
<Resource source="CSA" title="Breadth First Search" url="breadth_first_search" starred>interactive</Resource>
<Resource source="CPH" title="12.2 - Breadth-First Search" starred></Resource>
<Resource source="CPH" title="12.2 - Breadth-First Search"></Resource>
<Resource source="PAPS" title="12.1 - Breadth-First Search"></Resource>
<Resource source="KA" title="Breadth First Search and Its Uses" url="https://www.khanacademy.org/computing/computer-science/algorithms/breadth-first-search/a/breadth-first-search-and-its-uses"></Resource>
<Resource source="cp-algo" title="Breadth First Search" url="graph/breadth-first-search.html"></Resource>
@ -43,7 +44,70 @@ export const metadata = {
## Implementation
<IncompleteSection />
<LanguageSection>
<CPPSection>
From the CSA article:
```cpp
#include <algorithm>
#include <fstream>
#include <iostream>
#include <queue>
using namespace std;
const int MAX_N = 100005;
vector<int> graph[MAX_N];
int dist[MAX_N];
bool visited[MAX_N];
void bfs(int startNode) {
dist[startNode] = 0;
queue<int> bfsQueue;
bfsQueue.push(startNode);
visited[startNode] = true;
while (!bfsQueue.empty()) {
int currentNode = bfsQueue.front();
bfsQueue.pop();
for (auto neighbour: graph[currentNode]) {
if (!visited[neighbour]) {
visited[neighbour] = true;
dist[neighbour] = dist[currentNode] + 1;
bfsQueue.push(neighbour);
}
}
}
}
int main() {
int N, M, v, x, y;
cin >> N >> M >> v;
for (int i = 1; i <= M; i += 1) {
cin >> x >> y;
graph[x].push_back(y);
}
for (int i = 1; i <= N; i += 1) {
dist[i] = -1;
}
bfs(v);
for (int i = 1; i <= N; i += 1) {
cout << dist[i] << " ";
}
return 0;
}
```
</CPPSection>
<JavaSection>
</JavaSection>
</LanguageSection>
## Example: Cow Navigation
@ -61,14 +125,14 @@ For each pair of cells in the grid, $(x, y)$ and $x_2, y_2$, add 16 <!-- wow tha
Given this new graph, we add edges between two "states" which are reachable from each other. For example, if we apply the "turn left" operation, we add an edge to the state where both cows directions turn left.
On this new graph, we can directly run a BFS, and retrieve the answer at ${N, N, N, N, x, y}$ where $x$ and $y$ represent directions.
<Warning>
Don't forget that once Bessie reaches the goal, she will ignore further commands. In the modified problem, if one of the two cows is at the goal, stop applying commands to her.
</Warning>
On this new graph, we can directly run a BFS, and retrieve the answer at ${N, N, N, N, x, y}$ where $x$ and $y$ represent directions.
(Ben - equivalent to existing editorial)
<!-- You can change it, but keep the section on making modifications to graphs before. Maybe replace with a problem w/o a good editorial? -->

View file

@ -3,7 +3,7 @@ id: cyc
title: Cycle Detection
author: Siyong Huang
prerequisites:
- Gold - Topological Sort
- toposort
description: "A simple cycle is a non-empty path of distinct edges that start and end at the same vertex such that no vertex appears more than once. Describes how to detect cycles in both directed and undirected graphs."
frequency: 0
---

View file

@ -1,10 +1,10 @@
---
id: dp
id: intro-dp
title: "Introduction to Dynamic Programming"
author: Michael Cao
prerequisites:
- Recursion
- Silver - Prefix Sums
prerequisites:
- complete-search
- prefix-sums
description: "Speeding up naive recursive solutions with memoization."
frequency: 4
---
@ -41,11 +41,12 @@ export const metadata = {
new Problem("AC", "Count Paths", "https://atcoder.jp/contests/dp/tasks/dp_h", "Easy", true, ["DP"], "dp[x][y] = number of paths up to the point (x,y) in grid"),
new Problem("Gold", "Cow Checklist", "670", "Easy", false, ["DP"], "dp[visited i Hs][visited j Gs][last cow visited on left/right] -> min energy"),
new Problem("Gold", "Radio Contact", "598", "Easy", false, ["DP"], "dp[up to ith step of Farmer John][up to jth step of bessie] = minimum distance"),
new Problem("Gold", "Why Did The Cow Cross the Road II", "598", "Normal", false, ["DP"], "dp[up to ith field on left side][up to jth field on right side] = maximum number of disjoint crosswalks"),
new Problem("Gold", "Why ... (nocross)", "718", "Normal", false, ["DP"], "dp[up to ith field on left side][up to jth field on right side] = maximum number of disjoint crosswalks"),
new Problem("Old Gold", "Palindromic Paths", "553", "Hard", false, ["DP"], "start from the middle, dp[row i][row j][length] = number of strings of length 2 * length + 1 with ends at row i and j"),
],
lis: [
new Problem("LC", "Longest Increasing Subsequence", "https://leetcode.com/problems/longest-increasing-subsequence/", "Easy", true, ["DP"], "dp[i] = LIS up to i, use binary search to decrease runtime from quadratic"),
new Problem("LC", "Longest Increasing Subsequence", "https://leetcode.com/problems/longest-increasing-subsequence/", "Very Easy", true, ["DP"], "dp[i] = LIS up to i, use binary search to decrease runtime from quadratic"),
new Problem("Kattis", "Longest Increasing Subsequence", "longincsubseq", "Easy", true, [], ""),
new Problem("Old Gold", "Cowjog", "496", "Easy", false, ["DP"], "direct application of longest increasing subsequence"),
new Problem("Plat", "Sort It Out", "865", "Very Hard", false, ["DP"], "component of kth largest LIS, read editorial for more details"),
],
@ -98,7 +99,7 @@ Interesting applications of "number of paths on a grid," some of which don't dir
## Longest Increasing Subsequence
The first problem requires a quadratic time complexity solution to Longset Increasing subsequence, but you should optimize it to $O(N \log N)$. Some of the problems in this section don't initially look like Longest Increasing Subsequence, but it ends up being the solution <Asterick>This can happen a lot, which is why it's a good idea to not focus on one topic unless you have a full solution</Asterick>.
Some of the problems in this section don't initially look like Longest Increasing Subsequence, but it ends up being the solution. <Asterisk>This can happen a lot, which is why it's a good idea to not focus on one topic unless you have a full solution</Asterisk>
<Problems problems={metadata.problems.lis} />

View file

@ -3,10 +3,10 @@ id: dp-trees
title: "Dynamic Programming on Trees"
author: Michael Cao
prerequisites:
- Silver - Depth First Search
- Gold - Introduction to Dynamic Programming
- dfs
- intro-dp
description: "Using subtrees as subproblems."
frequency: 1
frequency: 2
---
import { Problem } from "../models";
@ -22,6 +22,7 @@ export const metadata = {
new Problem("AC", "Independent Set", "https://atcoder.jp/contests/dp/tasks/dp_p", "Easy", false, [], ""),
new Problem("Gold", "Barn Painting", "766", "Easy", false, ["DP"], "similar to independent set on tree"),
new Problem("ojuz", "COCI - Dzumbus", "COCI19_dzumbus", "Hard", false, [], ""),
new Problem("Gold", "Directory Traversal", "814", "Normal", false, [], ""),
new Problem("Gold", "Delegation", "1019", "Hard", false, ["Greedy"], ""),
new Problem("Plat", "Delegation", "1020", "Hard", false, ["DP", "Binary Search"], ""),
new Problem("CF", "Ostap & Tree", "problemset/problem/735/E", "Hard", false, ["DP"], "$O(NK)$ I think"),
@ -39,6 +40,8 @@ export const metadata = {
<Resource source="Philippines" title="DP on Trees and DAGs" url="https://noi.ph/training/weekly/week5.pdf">lol this code format is terrible </Resource>
</Resources>
## Sample Solution
## Solving for All Roots
<Problems problems={metadata.problems.allRoots} />

View file

@ -3,7 +3,7 @@ id: dsu
title: "Disjoint Set Union"
author: Benjamin Qi, Michael Cao
prerequisites:
- Silver - Depth First Search
- dfs
description: "The Disjoint Set Union (DSU) data structure allows you to add edges to an initially empty graph and test whether two vertices of the graph are connected."
frequency: 3
---
@ -18,6 +18,7 @@ export const metadata = {
general: [
new Problem("Gold", "Closing the Farm", "646", "Easy", false, [], "Simulate process in reverse and maintain the # of connected components. Similar to [CSES Network Breakdown](https://cses.fi/problemset/task/1677)"),
new Problem("Gold", "Mootube", "789", "Normal", false, [], "Answer queries in decreasing order of $k$. Maintain size of each connected component. Same as [CSES Road Construction](https://cses.fi/problemset/task/1676)"),
new Problem("Gold", "Favorite Colors", "1042", "Very Hard", false, ["DSU"], "Small to large merging is mentioned in the editorial, but we were unable to break solutions that just merged naively. Alternatively, just merge linked lists in $O(1)$ time."),
],
}
};
@ -28,9 +29,9 @@ export const metadata = {
<Resources>
<Resource source="CSA" title="Disjoint Data Sets" url="disjoint_data_sets" starred>both optimizations, diagrams</Resource>
<Resource source="PAPS" title="11.1 - Disjoint Sets" starred>both optimizations, no diagrams</Resource>
<Resource source="CPH" title="15.2 - Union-Find">small to large, diagrams</Resource>
<Resource source="IUSACO" title="10.6 - Disjoint-Set Data Structure">path compression, diagrams</Resource>
<Resource source="PAPS" title="11.1 - Disjoint Sets">both optimizations, no diagrams</Resource>
<Resource source="TC" title="Disjoint Set Data Structures" url="disjoint-set-data-structures">diagrams</Resource>
<Resource source="CPC" title="3 - Data Structures" url="03_data_structures"></Resource>
</Resources>
@ -46,7 +47,32 @@ Note: You may prefer to use this in place of DFS for computing connected compone
## Implementations
<IncompleteSection />
Check PAPS for the explanation. `e[x]` contains the negation of the size of $x$'s component if $x$ is the representative of its component, and the parent of $x$ otherwise.
<LanguageSection>
<CPPSection>
<resource source="Benq (from KACTL)" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/graphs%20(12)/DSU/DSU%20(7.6).h"> </resource>
```cpp
struct DSU {
vi e; void init(int N) { e = vi(N,-1); }
// get representive component, uses path compression
int get(int x) { return e[x] < 0 ? x : e[x] = get(e[x]); }
bool sameSet(int a, int b) { return get(a) == get(b); }
int size(int x) { return -e[get(x)]; }
bool unite(int x, int y) { // union by size
x = get(x), y = get(y); if (x == y) return 0;
if (e[x] > e[y]) swap(x,y);
e[x] += e[y]; e[y] = x; return 1;
}
};
```
</CPPSection>
</LanguageSection>
## Problems

View file

@ -5,7 +5,7 @@ author: Benjamin Qi
description: "Introduces gp_hash_table."
frequency: 1
prerequisites:
- Bronze - Unordered Maps & Sets
- unordered
---
import { Problem } from "../models";

View file

@ -3,7 +3,8 @@ id: intro-nt
title: "Introductory Number Theory"
author: Darren Yao, Michael Cao
prerequisites:
- Gold - Introduction to Dynamic Programming
- intro-dp
- binary representation
description: Introduces divisibility and modular arithmetic.
frequency: 1
---
@ -13,21 +14,24 @@ import { Problem } from "../models";
export const metadata = {
problems: {
sample: [
new Problem("CF", "VK Cup Wildcard R1 C - Prime Factorization", "problemset/problem/162/C", "Intro", false, []),
new Problem("CF", "VK Cup Wildcard R1 C - Prime Factorization", "problemset/problem/162/C", "Intro|Very Easy", false, []),
],
kat: [
new Problem("Kattis","Modular Arithmetic", "modulararithmetic"),
],
general: [
new Problem("AC", "Div Game", "https://atcoder.jp/contests/abc169/tasks/abc169_d", "Easy", false, ["Prime Factorization"], "Prime factorize the given number. Consider each prime in the factorization separately. For each prime, decrement the exponent by 1 the first time, 2 the second time, and so on, until we can no longer continue without repeating a previously used exponent."),
new Problem("CF", "Orac and LCM", "contest/1349/problem/A", "Normal", false, ["Prime Factorization"], "Prime factorize each number. For each prime, the second-to-lowest exponent of the prime that occurs in any of the numbers in the input is the exponent of this prime that will appear in the final answer."),
new Problem("Gold", "Cow Poetry", "897", "Normal", false, ["Knapsack", "Exponentiation"], "First consider the case where there are only two lines with the same class."),
new Problem("Gold", "Exercise", "1043", "Normal", false, ["Knapsack", "Prime Factorization"], "Prime factorize $K$."),
new Problem("CF", "Orac and LCM", "contest/1349/problem/A", "Normal", false, ["Prime Factorization"], "Prime factorize each number. For each prime, the second-to-lowest exponent of the prime that occurs in any of the numbers in the input is the exponent of this prime that will appear in the final answer.")
],
}
};
## Additional Resources
<Resources>
<Resource source="IUSACO" title="13 - Elementary Number Theory">module is based off this</Resource>
<Resource source="AoPS" title="Alcumus" url="https://artofproblemsolving.com/alcumus/problem" starred>practice problems, set focus to number theory!</Resource>
<Resource source="AoPS" title ="Intro to NT" url="https://artofproblemsolving.com/store/item/intro-number-theory?gtmlist=Bookstore_AoPS_Side">good book :D</Resource>
<Resource source="CPH" title="21.1, 21.2 - Number Theory">primes and factors, modular arithmetic</Resource>
<Resource source="PAPS" title="16 - Number Theory">same as below</Resource>
<Resource source="CF" title="CodeNCode - Number Theory Course" url="blog/entry/77137">lots of advanced stuff you don't need to know at this level</Resource>
@ -49,6 +53,12 @@ Now, we will discuss how to find the prime factorization of an integer.
![Pseudocode](factoralgorithm1.png)
<IncompleteSection>
replace with code in all langs?
</IncompleteSection>
This algorithm runs in $O(\sqrt{n})$ time, because the for loop checks divisibility for at most $\sqrt{n}$ values. Even though there is a while loop inside the for loop, dividing $n$ by $i$ quickly reduces the value of $n$, which means that the outer for loop runs less iterations, which actually speeds up the code.
Let's look at an example of how this algorithm works, for $n = 252$.
@ -57,10 +67,11 @@ Let's look at an example of how this algorithm works, for $n = 252$.
At this point, the for loop terminates, because $i$ is already 3 which is greater than $\lfloor \sqrt{7} \rfloor$. In the last step, we add $7$ to the list of factors $v$, because it otherwise won't be added, for a final prime factorization of $\{2, 2, 3, 3, 7\}$.
## Divisibility
## GCD & LCM
### GCD
The **greatest common divisor (GCD)** of two integers $a$ and $b$ is the largest integer that is a factor of both $a$ and $b$. In order to find the GCD of two numbers, we use the Euclidean Algorithm, which is as follows:
The **greatest common divisor (GCD)** of two integers $a$ and $b$ is the largest integer that is a factor of both $a$ and $b$. In order to find the GCD of two numbers, we use the **Euclidean Algorithm**, which is as follows:
$$
\gcd(a, b) = \begin{cases}
@ -69,25 +80,60 @@ $$
\end{cases}
$$
This algorithm is very easy to implement using a recursive function in Java, as follows:
This algorithm is very easy to implement using a recursive function, as follows:
<LanguageSection>
<JavaSection>
```java
public int gcd(int a, int b){
if(b == 0) return a;
if (b == 0) return a;
return gcd(b, a % b);
}
```
For C++, use the built-in `__gcd(a,b)`. Finding the GCD of two numbers can be done in $O(\log n)$ time, where $n = \min(a, b)$.
</JavaSection>
<CPPSection>
```cpp
int GCD(int a, int b){
if (b == 0) return a;
return GCD(b, a % b);
}
```
For C++14 and below, use the built-in `__gcd(a,b)`. C++17 has built in `gcd(a,b)`.
</CPPSection>
</LanguageSection>
This function runs in $O(\log \max(a,b))$ time.
<IncompleteSection>
proof?
</IncompleteSection>
### LCM
The **least common multiple (LCM)** of two integers $a$ and $b$ is the smallest integer divisible by both $a$ and $b$.
The LCM can easily be calculated from the following property with the GCD:
$$
\operatorname{lcm}(a, b) = \frac{a \cdot b}{\gcd(a, b)}.
\operatorname{lcm}(a, b) = \frac{a \cdot b}{\gcd(a, b)}=\frac{a}{\gcd(a,b)}\cdot b.
$$
<Warning>
Dividing by $\gcd(a,b)$ first might prevent integer overflow.
</Warning>
If we want to take the GCD or LCM of more than two elements, we can do so two at a time, in any order. For example,
$$
@ -96,7 +142,11 @@ $$
## Modular Arithmetic
In **modular arithmetic**, instead of working with integers themselves, we work with their remainders when divided by $m$. We call this taking modulo $m$. For example, if we take $m = 23$, then instead of working with $x = 247$, we use $x \bmod 23 = 17$. Usually, $m$ will be a large prime, given in the problem; the two most common values are $10^9 + 7$, and $998\,244\,353$. Modular arithmetic is used to avoid dealing with numbers that overflow built-in data types, because we can take remainders, according to the following formulas:
<Resources>
<Resource source="David Altizio" title="Modular Arithmetic" url="https://davidaltizio.web.illinois.edu/ModularArithmetic.pdf" starred> </Resource>
</Resources>
In **modular arithmetic**, instead of working with integers themselves, we work with their remainders when divided by $m$. We call this taking modulo $m$. For example, if we take $m = 23$, then instead of working with $x = 247$, we use $x \bmod 23 = 17$. Usually, $m$ will be a large prime, given in the problem; the two most common values are $10^9 + 7$, and $998\,244\,353=119\cdot 2^{23}+1$. Modular arithmetic is used to avoid dealing with numbers that overflow built-in data types, because we can take remainders, according to the following formulas:
$$
(a+b) \bmod m = (a \bmod m + b \bmod m) \bmod m
@ -116,9 +166,17 @@ $$
### Modular Exponentiation
<Resources>
<Resource source="cp-algo" title="Binary Exponentiation" url="algebra/binary-exp.html"> </Resource>
</Resources>
**Modular Exponentiation** can be used to efficently compute $x ^ n \mod m$. To do this, let's break down $x ^ n$ into binary components. For example, $5 ^ {10}$ = $5 ^ {1010_2}$ = $5 ^ 8 \cdot 5 ^ 4$. Then, if we know $x ^ y$ for all $y$ which are powers of two ($x ^ 1$, $x ^ 2$, $x ^ 4$, $\dots$ , $x ^ {2^{\lfloor{\log_2n} \rfloor}}$, we can compute $x ^ n$ in $\mathcal{O}(\log n)$. Finally, since $x ^ y$ for some $y \neq 1$ equals $2 \cdot x ^ {y - 1}$, and $x$ otherwise, we can compute these sums efficently. To deal with $m$, observe that modulo doesn't affect multiplications, so we can directly implement the above "binary exponentiation" algorithm while adding a line to take results $\pmod m$.
Here is C++ code to compute $x ^ n \pmod m$:
Here is code to compute $x ^ n \pmod m$:
<LanguageSection>
<CPPSection>
```cpp
long long binpow(long long x, long long n, long long m) {
@ -134,11 +192,21 @@ long long binpow(long long x, long long n, long long m) {
}
```
</CPPSection>
</LanguageSection>
### Modular Inverse
Under a **prime** modulus, division can be performed using modular inverses.
We'll only consider **prime** moduli here. Division can be performed using modular inverses.
To find the modular inverse of some number, simply raise it to the power of $\mathrm{MOD} - 2$, where $\mathrm{MOD}$ is the modulus being used, using the function described above. (The reasons for raising the number to $\mathrm{MOD} - 2$ can be explained with **Euler's theorem**, but we will not explain the theorem here).
<Resources>
<Resource source="cp-algo" title="Modular Multiplicative Inverse" url="algebra/module-inverse.html">Various ways to take modular inverse</Resource>
</Resources>
#### With Exponentiation
To find the modular inverse of some number, simply raise it to the power of $\mathrm{MOD} - 2$, where $\mathrm{MOD}$ is the modulus being used, using the function described above. (The reasons for raising the number to $\mathrm{MOD} - 2$ can be explained with **Fermat's Little Theorem**, but we will not explain the theorem here).
The modular inverse is the equivalent of the reciprocal in real-number arithmetic; to divide $a$ by $b$, multiply $a$ by the modular inverse of $b$.
@ -146,16 +214,16 @@ Because it takes $\mathcal{O}(\log \mathrm{MOD})$ time to compute a modular inve
Also, one must always ensure that they do not attempt to divide by 0. Be aware that after applying modulo, a nonzero number can become zero, so be very careful when dividing by non-constant values.
<Warning>
The above only works for **prime** modulos. Modulo inverses are *much more nuanced* for composite modulos.
</Warning>
(Ben - could have another module explaining modular inverses)
<IncompleteSection />
#### With Extended Euclidean Algorithm
<Optional>
See the module in the [Advanced](../adv/extend-euclid) section.
</Optional>
## Problems
<Problems problems={metadata.problems.general} />

View file

@ -27,8 +27,6 @@ export const metadata = {
}
};
## Sample
<Problems problems={metadata.problems.standard} />
## Resources
@ -41,6 +39,13 @@ export const metadata = {
<Resource source="cp-algo" title="Kruskal's with DSU" url="graph/mst_kruskal_with_dsu.html"></Resource>
</Resources>
## Implementation
### Kruskal's
### Prim's
## USACO Problems
<Problems problems={metadata.problems.general} />

View file

@ -1,92 +0,0 @@
---
id: PURQ
title: "Point Update Range Query"
author: Benjamin Qi
prerequisites:
- Gold - Point Update Range Sum
- Gold - Max Suffix Query with Insertions Only
description: "Range queries for any associative operation over an array with updates using segment tree."
frequency: 1
---
import { Problem } from "../models";
export const metadata = {
problems: {
sample: [
new Problem("CSES", "Range Minimum Queries II", "1649", "Intro|Easy", false, ["PURQ"], ""),
],
general: [
new Problem("YS", "Point Set Range Composite", "point_set_range_composite", "Easy", false, ["PURQ"], "Order of operations matters!"),
new Problem("Plat", "Slingshot", "816", "Hard", false, ["PURQ"], ""),
]
}
};
<Problems problems={metadata.problems.sample} />
<br/>
A **segment tree** allows you to do point update and range query in $O(\log N)$ time each for any associative operation.
## Resources
<Info title="Pro Tip">
For gold, you only need to know the basics (ex. sum, min queries). You can skip more advanced applications such as **lazy propagation** for now.
</Info>
<Resources>
<Resource source="CSA" title="Segment Trees" url="segment_trees" starred>interactive</Resource>
<Resource source="CPH" title="9.3 - Segment Trees" starred>same implementation as below</Resource>
<Resource source="CPC" title="3 - Data Structures" url="03_data_structures" starred>see slides after union-find</Resource>
<Resource source="cp-algo" title="Segment Tree" url="data_structures/segment_tree.html" starred></Resource>
<Resource source="PAPS" title="11.2.3 - Segment Trees"></Resource>
</Resources>
### Implementations
<Resources>
<Resource source="CF" title="AICash - Efficient and easy segment trees" url="blog/entry/18051" starred>simple implementation</Resource>
<Resource source="Benq" title="SegTree" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/SegTree%20(9.2).h">based off above</Resource>
</Resources>
<LanguageSection>
<CPPSection>
```cpp
template<class T> struct Seg { // comb(ID,b) = b
const T ID = 0; T comb(T a, T b) { return a+b; }
int n; vector<T> seg;
void init(int _n) { n = _n; seg.assign(2*n,ID); }
void pull(int p) { seg[p] = comb(seg[2*p],seg[2*p+1]); }
void upd(int p, T val) { // set val at position p
seg[p += n] = val; for (p /= 2; p; p /= 2) pull(p); }
T query(int l, int r) { // sum on interval [l, r]
T ra = ID, rb = ID;
for (l += n, r += n+1; l < r; l /= 2, r /= 2) {
if (l&1) ra = comb(ra,seg[l++]);
if (r&1) rb = comb(seg[--r],rb);
}
return comb(ra,rb);
}
};
```
</CPPSection>
<JavaSection>
</JavaSection>
</LanguageSection>
## Problems
Can also try solving some of tasks from both prerequisite articles.
<Problems problems={metadata.problems.general} />

View file

@ -3,8 +3,8 @@ id: PURS
title: "Point Update Range Sum"
author: Benjamin Qi
prerequisites:
- Silver - Prefix Sums
description: "Introducing Binary Indexed Trees & Indexed Sets (C++ only)."
- prefix-sums
description: "Introducing Segment Trees, Binary Indexed Trees, and Indexed Sets (C++ only)."
frequency: 3
---
@ -12,6 +12,10 @@ import { Problem } from "../models";
export const metadata = {
problems: {
seg: [
new Problem("CSES", "Range Minimum Queries II", "1649", "Intro|Easy", false, ["PURQ"], ""),
new Problem("YS", "Point Set Range Composite", "point_set_range_composite", "Easy", false, ["PURQ"], "Order of operations matters!"),
],
sample: [
new Problem("YS", "Point Add Range Sum", "point_add_range_sum", "Easy", false, ["PURS"], ""),
new Problem("CSES", "Range Sum Queries II", "1648", "Easy", false, ["PURS"], "Can also do range XOR queries w/ update."),
@ -24,7 +28,7 @@ export const metadata = {
new Problem("CSES", "Increasing Subsequence II", "1748", "Easy", false, undefined, ""),
new Problem("CSES", "Intersection Points", "1740", "Easy", false, undefined, ""),
new Problem("Kattis", "Mega Inversions", "megainversions", "Easy", false, undefined, "also just inversion counting"),
new Problem("HackerEarth", "Twin Permutations", "https://www.hackerearth.com/practice/data-structures/advanced-data-structures/fenwick-binary-indexed-trees/practice-problems/algorithm/mancunian-and-twin-permutations-d988930c/description/", "Easy", false, undefined, "Offline 2D queries can be done with a 1D data structure"),
new Problem("HE", "Twin Permutations", "https://www.hackerearth.com/practice/data-structures/advanced-data-structures/fenwick-binary-indexed-trees/practice-problems/algorithm/mancunian-and-twin-permutations-d988930c/description/", "Easy", false, undefined, "Offline 2D queries can be done with a 1D data structure"),
new Problem("CSES", "Distinct Values Queries", "1734", "Normal", false, undefined, "Do queries in increasing order of $a$."),
new Problem("CSES", "Robot Path", "1742", "Hard", false, undefined, ""),
],
@ -42,14 +46,76 @@ export const metadata = {
}
};
## Binary Indexed Tree
<Problems problems={metadata.problems.sample} />
A *Binary Indexed Tree* (aka *Fenwick Tree*) allows you to perform the following tasks in $O(\log N)$ time each on an array of size $N$:
<br />
Most gold range query problems require you to support following tasks in $O(\log N)$ time each on an array of size $N$:
- Update the element at a single position (point).
- Query the sum of a prefix of the array.
- Query the sum of some consecutive subarray.
Both **segment trees** and **binary indexed trees** can accomplish this.
## Segment Tree
<Problems problems={metadata.problems.seg} />
A **segment tree** allows you to do point update and range query in $O(\log N)$ time each for **any** associative operation, not just summation.
### Resources
<Info title="Pro Tip">
You can skip more advanced applications such as **lazy propagation** for now. They will be covered in [platinum](../plat/seg-ext).
</Info>
<Resources>
<Resource source="CSA" title="Segment Trees" url="segment_trees" starred>interactive</Resource>
<Resource source="CPH" title="9.3 - Segment Trees" starred>same implementation as below</Resource>
<Resource source="CPC" title="3 - Data Structures" url="03_data_structures" starred>see slides after union-find</Resource>
<Resource source="cp-algo" title="Segment Tree" url="data_structures/segment_tree.html" starred></Resource>
<Resource source="PAPS" title="11.2.3 - Segment Trees"></Resource>
</Resources>
### Implementations
<Resources>
<Resource source="CF" title="AICash - Efficient and easy segment trees" url="blog/entry/18051" starred>simple implementation</Resource>
<Resource source="Benq" title="SegTree" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/SegTree%20(9.2).h">based off above</Resource>
</Resources>
<LanguageSection>
<CPPSection>
```cpp
template<class T> struct Seg { // comb(ID,b) = b
const T ID = 0; T comb(T a, T b) { return a+b; }
int n; vector<T> seg;
void init(int _n) { n = _n; seg.assign(2*n,ID); }
void pull(int p) { seg[p] = comb(seg[2*p],seg[2*p+1]); }
void upd(int p, T val) { // set val at position p
seg[p += n] = val; for (p /= 2; p; p /= 2) pull(p); }
T query(int l, int r) { // sum on interval [l, r]
T ra = ID, rb = ID;
for (l += n, r += n+1; l < r; l /= 2, r /= 2) {
if (l&1) ra = comb(ra,seg[l++]);
if (r&1) rb = comb(seg[--r],rb);
}
return comb(ra,rb);
}
};
```
</CPPSection>
</LanguageSection>
## Binary Indexed Tree
Implementation is shorter than segment tree, but maybe more confusing at first glance.
### Resources
@ -62,14 +128,19 @@ A *Binary Indexed Tree* (aka *Fenwick Tree*) allows you to perform the following
### Implementations
<LanguageSection>
<CPPSection>
<Resources>
<Resource source="CF" title="mouse_wireless - Multi-dimensional BITs with Templates" url="https://codeforces.com/blog/entry/64914" starred></Resource>
<Resource source="Benq" title="BIT" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BIT%20(9.2).h">based off above</Resource>
</Resources>
(implementation)
</CPPSection>
</LanguageSection>
<IncompleteSection />
## Finding $k$-th Element
@ -78,7 +149,6 @@ Suppose that we want a data structure that supports all the operations as a `set
- `order_of_key(x)`: counts the number of elements in the set that are strictly less than `x`.
- `find_by_order(k)`: similar to `find`, returns the iterator corresponding to the `k`-th lowest element in the set (0-indexed).
### Indexed Set
Luckily, such a built-in data structure already exists in C++.
@ -121,15 +191,20 @@ int main() {
Note that if it were not the case that all elements of the input array were distinct, then this code would be incorrect since `Tree<int>` would remove duplicates. Instead, we would use an indexed set of pairs (`Tree<pair<int,int>>`), where the first element of each pair would denote the value while the second would denote the position of the value in the array.
### Using a BIT
### With a BIT
Assumes all updates are in the range $[1,N]$.
<Resources>
<Resource source="CF" url="blog/entry/11275" title="adamant - About Ordered Set" starred> log N </Resource>
<Resource source="GFG" url="order-statistic-tree-using-fenwick-tree-bit" title="Order Statistic Tree using BIT"> log^2 N </Resource>
</Resources>
<!-- <Resource source="GFG" url="order-statistic-tree-using-fenwick-tree-bit" title="Order Statistic Tree using BIT"> log^2 N </Resource> -->
### With a Segment Tree
Covered in [platinum](../plat/seg-ext).
## Practice Problems
<Problems problems={metadata.problems.practice} />

View file

@ -3,7 +3,8 @@ id: sp
title: "Shortest Paths with Non-Negative Edge Weights"
author: Benjamin Qi
prerequisites:
- Gold - Breadth First Search
- bfs
- stacks-queues
description: "Introduces Dijkstra's Algorithm for a single source shortest path as well as Floyd-Warshall for All-Pairs Shortest Path."
frequency: 3
---
@ -20,11 +21,11 @@ export const metadata = {
new Problem("CSES", "Flight Routes", "1196", "Easy", false, ["SP"], "$k$ smallest paths"),
new Problem("CSES", "Investigation", "1202", "Easy", false, ["SP"], ""),
new Problem("Gold", "Milk Pumping", "969", "Easy", false, ["SP"], ""),
new Problem("Gold", "Visit FJ", "717", "Easy", false, ["SP"], ""),
new Problem("Gold", "Why ... (visitfj)", "717", "Easy", false, ["SP"], ""),
new Problem("Gold", "Shortcut", "899", "Easy", false, ["SP"], ""),
new Problem("Gold", "Fine Dining", "861", "Easy", false, ["SP"], ""),
new Problem("Kattis", "Robot Turtles", "robotturtles", "Easy", false, ["SP"], ""),
new Problem("Kat", "Lane Switching", "https://open.kattis.com/contests/acpc17open/problems/laneswitching", "Normal", false, ["SP"], ""),
new Problem("Kattis", "Lane Switching", "https://open.kattis.com/contests/acpc17open/problems/laneswitching", "Normal", false, ["SP"], ""),
],
apspSam: [
new Problem("CSES", "Shortest Routes II", "1672", "Easy", false, ["APSP"], ""),

View file

@ -5,6 +5,7 @@ author: Benjamin Qi
description: "Range queries for any associative operation over a static array."
frequency: 1
---
import { Problem } from "../models";
export const metadata = {

View file

@ -3,8 +3,7 @@ id: springboards
title: "Max Suffix Query with Insertions Only"
author: Benjamin Qi
prerequisites:
- Silver - More with Maps & Sets
- Silver - Amortized Analysis
- harder-ordered
description: "A solution to USACO Gold - Springboards."
frequency: 1
---

View file

@ -4,6 +4,8 @@ title: "String Hashing"
author: Benjamin Qi
description: "Quickly test equality of substrings with a small probability of failure."
frequency: 1
prerequisites:
- intro-nt
---
import { Problem } from "../models";
@ -16,6 +18,7 @@ export const metadata = {
general: [
new Problem("CSA", "Palindromic Partitions", "palindromic-partitions", "Easy", false, ["Greedy", "Hashing"], ""),
new Problem("CF", "Palindromic Characteristics", "problemset/problem/835/D", "Easy", false, ["DP", "Hashing"], ""),
new Problem("Gold", "Lights Out", "599", "Normal", false, [], "oops you don't actually need hashing to pass ..."),
new Problem("CF", "Liar", "problemset/problem/822/E", "Hard", false, ["DP", "Hashing"], ""),
new Problem("Plat", "Bull in a China Shop", "649", "Insane", false, ["Hashing"], "what a terrible problem, incorrect constraints"),
],

View file

@ -1,7 +1,7 @@
---
id: tree-euler
title: "Euler Tour Technique"
author: "?"
author: "Benjamin Qi"
prerequisites:
- Silver - Depth First Search
- Gold - Static Range Queries
@ -26,7 +26,7 @@ export const metadata = {
new Problem("Gold", "Cow Land", "921", "Normal", false, ["Euler-Tree","PURS", "HLD"], ""),
new Problem("Gold", "Milk Visits", "970", "Normal", false, ["Euler-Tree", "LCA"], ""),
new Problem("Plat", "Promotion Counting", "696", "Normal", false, ["Euler-Tree","PURS"], ""),
new Problem("ojuz", "IOI Regions", "IOI09_regions", "Hard", false, ["Euler-Tree", "Binary Search"], ""),
new Problem("ojuz", "IOI - Regions", "IOI09_regions", "Hard", false, ["Euler-Tree", "Binary Search"], ""),
new Problem("Plat", "Snow-Cow", "973", "Hard", false, ["Euler-Tree","PURS"], ""),
]
}

View file

@ -22,7 +22,6 @@ export const metadata = {
new Problem("Plat", "Promotion Counting", "696", "Normal", false, ["Merging", "Indexed Set"], ""),
new Problem("Plat", "Disruption", "842", "Normal", false, ["Merging"]),
new Problem("POI", "Tree Rotations", "https://szkopul.edu.pl/problemset/problem/sUe3qzxBtasek-RAWmZaxY_p/site/?key=statement", "Normal", false, ["Merging", "Indexed Set"], ""),
new Problem("Gold", "Favorite Colors", "1042", "Hard", false, ["DSU"], "Small to large merging is mentioned in the editorial, but we were unable to break solutions that just merged naively. Alternatively, just merge linked lists in $O(1)$ time."),
],
}
};

View file

@ -13,6 +13,9 @@ import { Problem } from "../models";
export const metadata = {
problems: {
oops: [
new Problem("Plat", "Slingshot", "816", "Hard", false, ["PURQ"], ""),
]
walkSam: [
new Problem("CSES", "Hotel Queries", "1143", "Easy", false, ["PURQ"], "walk"),
],
@ -39,6 +42,10 @@ export const metadata = {
}
};
## ??
<Problems problems={metadata.problems.oops} />
## Walking on a Segment Tree
<Problems problems={metadata.problems.walkSam} />

View file

@ -1,7 +1,7 @@
---
id: string-search
title: "String Searching"
author: Benjamin Qi
author: Benjamin Qi, Siyong Huang
prerequisites:
- Silver - Depth First Search
description: Knuth-Morris-Pratt and Z Algorithms (and a few more related topics).
@ -10,13 +10,29 @@ frequency: 1
export const metadata = {
problems: {
sample: [
new Problem("YS", "Set XOR-Min", "set_xor_min", "Easy", false, [], ""),
],
KMP: [
new Problem("Kattis", "String Matching", "problems/stringmatching", "Easy", false, ["Strings"], "Naive KMP works. Just be careful about I/O"),
new Problem("POJ", "(USACO Gold 05) Cow Patterns", "http://poj.org/problem?id=3167", "Hard", false, ["Strings"], "Run KMP, except each state needs to be considered as not only a length, but also mapping of pattern to # of spots"),
],
Z: [
new Problem("YS", "Z Algorithm", "zalgorithm", "Easy", false, [], ""),
new Problem("CF", "Concatenation with Intersection", "contest/1313/problem/E", "Hard", false, [], "")
new Problem("CF", "Vasya and Big Integers", "contest/1051/problem/E", "Normal", false, ["Strings", "DP"], "dp[i] = answer for suffix [i, N]. Transitions form some continuous range and can be determined using two-pointer and z-function for long integer comparison"),
new Problem("CF", "Concatenation with Intersection", "contest/1313/problem/E", "Hard", false, [], ""),
],
mana: [
new Problem("CF", "Sonya and Matrix Beauty", "contest/1080/problem/E", "Normal", false, ["Strings"], "N^3 solution. Brute force every range of rows, and run manachers along the columns"),
new Problem("CF", "Prefix-Suffix Palindrome", "contest/1326/problem/D2", "Normal", false, ["Strings"], "Find longest prefix that is mirror of suffix. Add these to the answer. Then use manachers/any other palindrome detection algorithm to determine the longest prefix/suffix palindrome of the remaining string."),
new Problem("CF", "Palisection", "contest/17/problem/E", "Hard", false, ["Strings", "Prefix Sums"], ""),
],
trie: [
new Problem("YS", "Set XOR-Min", "set_xor_min", "Easy", false, [], ""),
new Problem("CF", "The Fair Nut and Strings", "contest/1083/problem/B", "Normal", false, ["Strings"], "Answer is max number of nodes in trie. Count the max number of nodes on each depth of the trie, and add it all up."),
new Problem("CF", "Tree and XOR", "contest/1055/problem/F", "Hard", false, ["Trie", "Tree"], "Path xor from node u to node v is equal to root_xor(u) xor root_xor(v). Sort these root_xor values. Use implicit trie over all values of root_xor(u) ^ root_xor(v) and traverse the trie. Each node of the trie can be treated as a set of pairs of ranges in the sorted root_xor array, such that the xor of two values in each range results in a prefix of that trie node. (Read editorial for clear explanation)"),
],
aho: [
new Problem("Gold", "Censoring (Gold)", "533", "Normal", false, ["Strings"], "Build aho-corasick over all censored words"),
new Problem("CF", "You Are Given Some Strings...", "contest/1202/problem/E", "Normal", false, ["Strings"], "Count number of pairs for each cut-off point. Run aho-corasick twice: normally, and reversed"),
],
pal: [
new Problem("ojuz", "Palindrome", "APIO14_palindrome", "Easy", false, [], ""),
]
@ -33,6 +49,9 @@ export const metadata = {
## KMP
**Knuth-Morris-Pratt**, or **KMP**, is a linear time string comparison algorithm that matches prefixes.
Specifically, it computes the longest substring that is both a prefix and suffix of a string, and it does so for every prefix of a given string.
<Resources>
<Resource source="cp-algo" title="Prefix Function" url="prefix-function.html"></Resource>
<Resource source="PAPS" title="14.2 - String Matching"></Resource>
@ -40,10 +59,11 @@ export const metadata = {
<Resource source="TC" title="String Searching" url="introduction-to-string-searching-algorithms"></Resource>
</Resources>
<Problems problems={metadata.problems.KMP} />
## Z Algorithm
<Problems problems={metadata.problems.Z} />
The **Z-Algorithm** is another linear time string comparison algorithm like KMP, but instead finds the longest common prefix of a string and all of its suffixes.
<Resources>
<Resource source="cp-algo" title="Z Function" url="z-function.html"></Resource>
@ -51,19 +71,32 @@ export const metadata = {
<Resource source="CF" title="Z Algorithm" url="blog/entry/3107"></Resource>
</Resources>
<Problems problems={metadata.problems.Z} />
## Manacher
**Manacher's Algorithm** is functionally similarly to the **Z-Algorithm** and can compute information about palindromes.
It can determine the longest palindrome centered at each character.
<Resources>
<Resource source="HR" title="Manacher's Algorithm" url="https://www.hackerrank.com/topics/manachers-algorithm"></Resource>
<Resource source="CF" title="adamant - Manacher's algorithm and code readability" url="blog/entry/12143" starred>shorter code</Resource>
<Resource source="cp-algo" title="Manacher's Algorithm" url="string/manacher.html"></Resource>
</Resources>
<Info title="Don't Forget!">
If s[l, r] is a palindrome, then s[l+1, r-1] is as well.
</Info>
<Problems problems={metadata.problems.mana} />
# Multiple Strings
## Tries
<Problems problems={metadata.problems.sample} />
A **trie** is a tree-like data structure that stores strings. Each node is a string, and each edge is a character.
The root is the empty string, and every node is represented by the characters along the path from the root to that node.
This means that every prefix of a string is an ancestor of that string's node.
<Resources>
<Resource source="CPH" title="26.2"></Resource>
@ -71,20 +104,34 @@ export const metadata = {
<Resource source="PAPS" title="14.1 - Tries"></Resource>
</Resources>
<Problems problems={metadata.problems.trie} />
## Aho-Corasick
**Aho-Corasick** is the combination of **trie** and **KMP**. It is essentially a trie with KMP's "fail" array.
<Warning>
Build the entire trie first, and then run a *BFS* to construct the fail array.
</Warning>
<Resources>
<Resource source="cp-algo" title="Aho Corasick" url="string/aho_corasick.html"></Resource>
<Resource source="CF" title="adamant - Aho-Corasick" url="blog/entry/14854"></Resource>
<Resource source="GFG" title="Aho-Corasick for Pattern Searching" url="aho-corasick-algorithm-pattern-searching"></Resource>
</Resources>
<Problems problems={metadata.problems.aho} />
## Palindromic Tree
<Problems problems={metadata.problems.pal} />
(IDK what this is :/ - Siyong)
<Resources>
<Resource source="CF" title="adamant - Palindromic Tree" url="blog/entry/13959"></Resource>
</Resources>
<Problems problems={metadata.problems.pal} />
DMOJ thing

View file

@ -0,0 +1,179 @@
---
id: extend-euclid
title: "Extended Euclidean Algorithm"
author: Benjamin Qi
prerequisites:
- intro-nt
description: "?"
frequency: 1
---
import { Problem } from "../models";
export const metadata = {
problems: {
kat: [
new Problem("Kattis","Modular Arithmetic", "modulararithmetic"),
],
crt: [
new Problem("Kattis","Chinese Remainder", "chineseremainder"),
new Problem("Kattis","Chinese Remainder (non-relatively prime moduli)", "generalchineseremainder"),
],
}
};
## Euclidean Algorithm
<Resources>
<Resource source="cp-algo" url="algebra/euclid-algorithm.html" title="Euclidean"> </Resource>
</Resources>
The original Euclidean Algorithm computes $\gcd(a,b)$ and looks like this:
<LanguageSection>
<CPPSection>
```cpp
ll euclid(ll a, ll b) {
for (;b;swap(a,b)) {
ll k = a/b;
a -= k*b;
}
return a; // gcd(a,b)
}
```
## Extended Euclidean Algorithm
<Resources>
<Resource source="cp-algo" url="algebra/extended-euclid-algorithm.html" title="Extended Euclidean" starred> </Resource>
<Resource source="Wikipedia" url="https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm" title="Extended Euclidean"> </Resource>
<Resource source="cp-algo" url="algebra/linear-diophantine-equation.html" title="Linear Diophantine Equation"> </Resource>
</Resources>
The extended Euclidean algorithm computes integers $x$ and $y$ such that
$$
ax+by=\gcd(a,b)
$$
</CPPSection>
</LanguageSection>
We can slightly modify the version of the Euclidean algorithm given above to return more information!
<LanguageSection>
<CPPSection>
```cpp
array<ll,3> extendEuclid(ll a, ll b) {
array<ll,3> x = {1,0,a}, y = {0,1,b};
// we know that 1*a+0*b=a and 0*a+1*b=b
for (;y[2];swap(x,y)) { // run extended Euclidean algo
ll k = x[2]/y[2];
F0R(i,3) x[i] -= k*y[i];
// keep subtracting multiple of one equation from the other
}
return x; // x[0]*a+x[1]*b=x[2], x[2]=gcd(a,b)
}
int main() {
FOR(a,1,101) FOR(b,1,101) {
auto x = extendEuclid(a,b);
int g = x[2];
assert(g == __gcd(a,b));
if (a != b) assert(abs(x[0]) <= b/g/2 && abs(x[1]) <= a/g/2);
}
}
```
</CPPSection>
</LanguageSection>
### Recursive Version
<LanguageSection>
<CPPSection>
```cpp
ll euclid(ll a, ll b) {
if (!b) return a;
return euclid(b,a%b);
}
```
</CPPSection>
</LanguageSection>
becomes
<LanguageSection>
<CPPSection>
```cpp
pl extendEuclid(ll a, ll b) { // returns {x,y}
if (!b) return {1,0};
pl p = extendEuclid(b,a%b); return {p.s,p.f-a/b*p.s};
}
```
</CPPSection>
</LanguageSection>
The pair will equal to the first two returned elements of the array in the iterative version. Looking at this version, we can prove by induction that when $a$ and $b$ are distinct positive integers, the returned pair $(x,y)$ will satisfy $|x|\le \frac{b}{2\gcd(a,b)}$ and $|y|\le \frac{a}{2\gcd(a,b)}$. Furthermore, there can only exist one pair that satisfies these conditions!
Note that this works when $a,b$ are quite large (say, $\approx 2^{60}$) and we won't wind up with overflow issues.
## Application: Modular Inverse
<Resources>
<Resource source="cp-algo" url="algebra/module-inverse.html" title="Modular Inverse"> </Resource>
</Resources>
<Problems problems={metadata.problems.kat} />
It seems that when multiplication / division is involved in this problem, $n^2 < \texttt{LLONG\_MAX}$.
<LanguageSection>
<CPPSection>
```cpp
ll invGeneral(ll a, ll b) {
array<ll,3> x = extendEuclid(a,b);
assert(x[2] == 1); // gcd must be 1
return x[0]+(x[0]<0)*b;
}
int main() {
FOR(b,1,101) F0R(a,101) if (__gcd(a,b) == 1) {
ll x = invGeneral(a,b);
assert((a*x-1)%b == 0);
assert(0 <= x && x < b);
}
}
```
</CPPSection>
</LanguageSection>
## Application: Chinese Remainder Theorem
<Resources>
<Resource source="cp-algo" url="algebra/linear_congruence_equation.html" title="Linear Congruence Equation"> </Resource>
<Resource source="cp-algo" url="algebra/chinese-remainder-theorem.html" title="Chinese Remainder Theorem"></Resource>
</Resources>
<Problems problems={metadata.problems.crt} />

View file

@ -137,7 +137,7 @@ const MODULE_ORDERING: {[key in SectionID]: Category[]} = {
{
name: "Dynamic Programming",
items: [
"dp",
"intro-dp",
]
},
{
@ -161,7 +161,6 @@ const MODULE_ORDERING: {[key in SectionID]: Category[]} = {
"SRQ",
"springboards",
"PURS",
"PURQ",
]
},
{
@ -273,6 +272,7 @@ const MODULE_ORDERING: {[key in SectionID]: Category[]} = {
{
name: "Misc. Topics",
items: [
"extend-euclid",
"critical",
"string-suffix",
"game-theory",

View file

@ -76,6 +76,7 @@ export const sourceTooltip = {
LC: 'LeetCode',
POI: 'Polish Olympiad in Informatics',
SO: 'StackOverflow',
KA: 'KhanAcademy',
'Old Bronze': 'USACO Platinum did not exist prior to 2015-16.',
'Old Silver': 'USACO Platinum did not exist prior to 2015-16.',
'Old Gold': 'USACO Platinum did not exist prior to 2015-16.',