diff options
author | Anthony Wang | 2020-07-21 23:09:13 +0000 |
---|---|---|
committer | GitHub | 2020-07-21 23:09:13 +0000 |
commit | 027dbdb35529d58efe8175350ca4d7810429ab0d (patch) | |
tree | 65767e88c27469b2cdac31cc327764d7bc8cfe11 | |
parent | 6db19b0243f6e533ba00ad81ee8479de27c1fd71 (diff) | |
parent | 40bc285712ea5c698e91e6a003fdf5ef4f11e5be (diff) |
Merge pull request #7 from thecodingwizard/master
Update to latest version
272 files changed, 21998 insertions, 8195 deletions
diff --git a/.prettierignore b/.prettierignore index 29fcced..2309b38 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,7 @@ .cache package.json -package-lock.json +yarn.lock public -content
\ No newline at end of file +content +docs +static
\ No newline at end of file diff --git a/Content Documentation.md b/Content Documentation.md deleted file mode 100644 index 96f9531..0000000 --- a/Content Documentation.md +++ /dev/null @@ -1,134 +0,0 @@ -## Content Formatting Documentation - -All modules are written in [Markdown](https://www.markdownguide.org/cheat-sheet/). There are special additions to the markdown that we have added to this site. -These special additions are still under development, so they may change frequently. -If you are confused about something, or if there's a certain feature that you want to add, reach out to Nathan Wang. - -You can use [StackEdit](https://stackedit.io/) to check that latex renders properly. - -### `ordering.ts` - -Located at `content/ordering.ts`, this file stores the ordering of the modules. Hopefully the format is self-explanatory -(it matches based on "slug"). Let Nathan Wang know if you have questions. - -### Frontmatter - -[Frontmatter](https://jekyllrb.com/docs/front-matter/) is the stuff in the beginning of each module that's surrounded -by three dashes. Frontmatter is written in [YAML](https://yaml.org/). It stores the "metadata" for each module. - -YAML formatting is _extremely strict_. Be careful about spaces. Additionally, escape special characters by wrapping the string with double quotes. - -- **ID**: _Required_. The ID of the module. Ex: `getting-started`, or `containers`. This ID is used to identify - the module, so make sure it is **unique** and **all lowercase with dashes only**. The URL will be generated based off this. -- **Title**: _Required_. The title of the module. Ex: `Getting Started` -- **Author**: _Required_. The author of the module. Ex: `Unknown` -- **Description**: _Required_. A short description of the module, similar to what [codecademy](https://www.codecademy.com/learn/paths/computer-science) has in their syllabus. Markdown/Latex does not work in the description field. -- **Prerequisites**: _Optional_. Any prerequisites for this module. (Coming soon: If you want to reference a module as a prerequisite, you can list the module ID.) - -### Problem Lists - -todo document this... Relevant files are `content/models.ts` and `src/components/markdown/Problems.tsx`. Hopefully, the existing mdx files can help you out... - -Problem constructor: - -```typescript -constructor( - public source: string, - public name: string, - public id: string, - public difficulty?: 'Intro' | 'Easy' | 'Normal' | 'Hard' | 'Very Hard', - public starred?: boolean, - public tags?: string[], - public sketch?: string, -) -``` - -Example usage: - -```markdown ---- -id: ds -title: Data Structures -author: Nathan Wang, Darren Yao, Benjamin Qi -description: Introductory problems using sets and maps. -prerequisites: - - Bronze - "Built-In C++ Containers" or "Built-In Java Collections" ---- - -import { Problem } from "../models" - -export const metadata = { -problems: { -standard: [ -new Problem("YS", "Associative Array", "associative_array", "Intro"), -new Problem("CSES", "Distinct Numbers", "1621", "Intro"), -new Problem("CSES", "Sum of Two Values", "1640", "Intro", false, [], "Can be solved without sets."), -new Problem("CSES", "Concert Tickets", "1091", "Easy", false, ["iterators"]), -new Problem("CSES", "Towers", "1073", "Easy", false, ["multiset", "greedy"]), -new Problem("CSES", "Traffic Lights", "1163", "Normal", false, ["set"]), -new Problem("CSES", "Room Allocation", "1164", "Normal", false, ["multiset", "greedy"]), -] -} -}; - -## Standard - -Do roughly the first half of the Sorting and Searching section in the [CSES Problem Set](https://cses.fi/problemset/). - -<problems-list problems={metadata.problems.standard} /> -``` - -### Spoilers - -Spoilers are collapsible elements that only show themselves when the user clicks on it. It's useful -when writing solutions to problems. - -``` -<spoiler title="Show Solution"> - -- Insert OP benq solution here - -</spoiler> -``` - -### Info Block - -``` -<info-block title="Insert Title Here"> - -**Markdown is Supported!!** - -</info-block> -``` - -### Optional Block - -``` -<optional-content title="Insert Title Here"> - -Fun fact: the title attribute is optional. - -</optional-content> -``` - -### Example Module - -``` ---- -id: getting-started -title: Getting Started -description: Welcome to the guide! We'll introduce what programming competitions are and how this guide is organized. -author: Nathan Wang -order: 1 -prerequisites: - - Dummy prerequisite - - running-cpp -problems: - - bronze_promote - - bronze_word - - bronze_paint - - bronze_square ---- - -# Hello World! -``` @@ -9,8 +9,8 @@ As much as possible, please try to keep the markdown files independent of the fr ## Documentation -- To get this site running locally, refer to the [Front End Documentation](Front%20End%20Documentation.md). -- For information regarding Content Writing, refer to the [Content Documentation](Content%20Documentation.md). +- To get this site running locally, refer to the [Front End Documentation](docs/Front%20End%20Documentation.md). +- For information regarding Content Writing, refer to the [Content Documentation](docs/Content%20Documentation.md). ## Tech Stack diff --git a/content/1_Intro/Choosing_Lang.mdx b/content/1_Intro/Choosing_Lang.mdx new file mode 100644 index 0000000..8014ff4 --- /dev/null +++ b/content/1_Intro/Choosing_Lang.mdx @@ -0,0 +1,62 @@ +--- +id: choosing-lang +title: Choosing a Language +author: Nathan Wang, Benjamin Qi +description: What languages you can use for programming contests. +--- + +The most popular languages that USACO supports are [C++11](https://en.wikipedia.org/wiki/C%2B%2B), [Java](https://en.wikipedia.org/wiki/Java_(programming_language)), and [Python 3](https://en.wikipedia.org/wiki/Python_(programming_language)). Pascal used to be popular in the past, but its use is rare now, and many programming contests have removed or are planning to remove support for it in the near future (in short: don't use it now). C is also supported, but it's essentially a strictly inferior version of C++ and doesn't have the built-in data structures that are often used. In general, we recommend the following: + + - **If you already know one of these languages, just use it.** + - If you know multiple languages, we recommend you pick C++ over Java, and Java over Python. + - **For Bronze, any language will do.** It is possible to receive full credit with C++, Java, and Python in Bronze. + - **For Silver<Asterisk>However, if Python is the only language you know / you really want to use Python, you can still pass Silver with Python.</Asterisk>, Gold, and Platinum, Python is not recommended**, because it is a slow language and lacks an ordered map. + - Check [Factors to Consider When Choosing a Language](/intro/factors-choosing) for more information. + +Keep in mind that it's easy to switch languages down the road. Don't get caught up on which language to choose. Just pick the one you feel most comfortable with! + +### Language References + +All of these are provided at the IOI aside from the additional C++ reference. + + - [C++](https://en.cppreference.com/w/) + - [Additional C++ Reference](http://www.cplusplus.com/) + - [Java](https://docs.oracle.com/javase/8/docs/api/overview-summary.html) + - [Python3](https://docs.python.org/3/reference/) + +## Resources for Learning How to Code + +If you aren't comfortable with basic coding yet, you can use the resources below. Let us know what works (or doesn't) for you. + +### General + +<Resources title="Learning to Code"> + <Resource title="Sololearn" url="https://www.sololearn.com/">courses for C++, Java, Python</Resource> + <Resource title="Codecademy" url="https://www.codecademy.com/">free courses for C++, Java, Python 2</Resource> +</Resources> + +<LanguageSection> + +<CPPSection> + +### C++ + +Use one of the resources above or below (or find your own) to learn C++. If you use Sololearn, you don't have to complete the full course; we recommend you finish everything up to (but not including) "More on Classes." + +<Resources title="C++"> + <Resource source="PAPS" title="2 - Programming in C++" starred>lots of examples, Kattis exercises</Resource> +</Resources> + +<Info title="Pro Tip"> + +You do not need to learn pointers (for now). Knowledge of structs and classes is useful but not required. + +</Info> + +</CPPSection> + +<JavaSection></JavaSection> +<PySection></PySection> + +</LanguageSection> + diff --git a/content/1_Intro/Code_Conventions.mdx b/content/1_Intro/Code_Conventions.mdx new file mode 100644 index 0000000..a2625a3 --- /dev/null +++ b/content/1_Intro/Code_Conventions.mdx @@ -0,0 +1,169 @@ +--- +id: code-con +title: Code Conventions +author: Nathan Wang, Benjamin Qi, Michael Cao, Nathan Chen +description: "What the code we provide should look like." +prerequisites: + - expected + - modules + - cpp-tips +--- + +The code we provide should follow the conventions below. Please note that you do _not_ have to copy our conventions; there is no "right" convention for coding! + +<LanguageSection> + +<CPPSection> + +If you find unrecognizable symbols or functions in `C++` code, note the template below; everything should compile assuming that the template below is included. + +</CPPSection> + +<JavaSection /> + +<PySection /> + +</LanguageSection> + +If any code does not compile or is hard to read, submit a complaint using the "Contact Us" button. + +## General + +Note that these conventions mostly apply to `C++` and `Java`. + +<Resources> + <Resource source = "CF" title = "Swift - Competitive C++ Manifesto" url="blog/entry/64218">most material directly from this blog</Resource> +</Resources> + + - Indenting with either tabs or 4 spaces is fine. + - Binary operators should be spaced on both sides. For example, `a+b` should be written as `a + b` and `x=a+b` should be written as `x = a + b`. + - Unary operators should only be spaced on the left side, ex. `x = -y`. + - Semicolons and commas should be spaced on the right side, ex. `f(a, b, c)`. + - Braces that open on one line should close on another. Always include braces, even if the code inside them is only one line. + - Parentheses should be spaced from the outside. For example, we would write `if (a == b) {` or `for (int i = 0; i < n; ++i) {`. + - Use `++x` rather than `x++` to increment a variable (ie. in a `for` loop). + - Start your `else` statement on the same line as the closing brace of your `if` statement, like so: + +<LanguageSection> + +<CPPSection> + +```cpp +if (a == b) { + ++a; +} else { + ++b; +} +``` + +</CPPSection> + +<JavaSection> + +```java +if (a == b) { + ++a; +} else { + ++b; +} +``` + +</JavaSection> + +<PySection> + +```py +if (a == b): + a += 1 +else + b += 1 +``` + +</PySection> + +</LanguageSection> + + + - Use `true / false` for boolean values, not `1 / 0`. + - Write comments to explain any uncommon library functions like the C++ functions `__builtin_ffs()` or `tie(a, b, c)`. + - Write comments to explain anything weird in general! :) + +## Template + +The "template" refers to code that is assumed to be in every file. + +<LanguageSection> + +<CPPSection> + +Templates in C++ can take advantange of more powerful features than the other contest languages, and they can be more customized to each competitor. Don't be afraid to write your own template or don't use one at all! Regardless, The USACO Guide will assume the use of the below template in any C++ Code. + +<!-- See [C++ Tips & Tricks](./cpp-tips) for explanations of the following code. --> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +using ll = long long; + +using vi = vector<int>; +#define pb push_back +#define rsz resize +#define all(x) begin(x), end(x) +#define sz(x) (int)(x).size() + +using pi = pair<int,int>; +#define f first +#define s second +#define mp make_pair + +void setIO(string name = "") { // name is nonempty for USACO file I/O + ios_base::sync_with_stdio(0); cin.tie(0); // see Fast Input & Output + freopen((name+".in").c_str(), "r", stdin); // see Input & Output + freopen((name+".out").c_str(), "w", stdout); +} + +// the code that we provide requires everything above this line to compile + +int main() { + setIO(); + +} +``` + +<!-- Any preferences? Also, this doesn't apply to the custom sorting module, make sure to include all 3! :) --> + +Additional C++ Conventions: + + - Avoid the use of macros (`#define`) other than those in the template above. + - Use `const` in C++ for variables that don't change through the code. + - Use `make_pair` rather than `{}` to store a pair. + - Use `auto` to increase readability (ex. when dealing with iterators). + - Use `struct` instead of `class`. <!-- Even if you are copy pasting a `struct`, space it out rather than squeezing it into one line. --> + - Use UpperCamelCase for custom structs (`SegTree, Point`) and lowerCamelCase for functions (`solve(), binarySearch()`). Constants should be fully capitalized (`INF, MOD, SOME_CONST`). + +</CPPSection> + +<JavaSection> + +A normal Java template will contain a main method, helpful imports, and some form of I/O (for USACO, this would probably be File I/O) + +```java +import java.io.*; //Imports necessary classes to read and write files +import java.util.*; //Imports many useful data structures and algorithms libraries + +public class Main { //Name your program whatever you like. Note the class name must match the file name (e.g. this file would be "Main.java") + + public static void main(String[] args) throws Exception { //The code below reads from stdin and also uses a slow input method. Read "Intro - Fast I/O" for other Java I/O techniques. + Scanner sc = new Scanner(System.in); + PrintWriter pw = new PrintWriter(System.out); + } +} + +``` + +</JavaSection> + +<PySection /> + +</LanguageSection> diff --git a/content/2_General/Contest_Strategy.md b/content/1_Intro/Contest_Strategy.mdx index d2cad95..9c34212 100644 --- a/content/2_General/Contest_Strategy.md +++ b/content/1_Intro/Contest_Strategy.mdx @@ -5,9 +5,7 @@ author: Nathan Chen description: General ideas on how to strategize during a USACO contest. --- -(pdf?) - -The best contest strategy is to go in knowing everything. However, only few people can do this, see [BenQ](https://codeforces.com/profile/benq) or [TMW](https://codeforces.com/profile/tmwilliamlin168). (I kid). +The best contest strategy is to go in knowing everything. However, only few people can do this; see [Benq](https://codeforces.com/profile/benq) or [tmw](https://codeforces.com/profile/tmwilliamlin168) (I kid). Once the timer starts, there's no more studying you can do, and managing your time wisely is the best thing you can do to maximize your score. @@ -21,9 +19,15 @@ You should read *all* the problems first (don't give up on any problems prematur Problem difficulties can be out of order, so keep that in mind before focusing down 3 hours on problem 1 and not getting anywhere. Do a good amount of thinking on each problem before deciding which ones to focus on and which to put aside. +<Info title="Pro Tip"> + +Historically, the first platinum problem has never been the hardest (though you shouldn't count on this). + +</Info> + ### Time allocation -It's the worst feeling in the world to sink 4 hours into a problem and not get it, only for another problem to have an easier solution. You have to use your own judgement in deciding what is likely solvable and what should be quit. Generally, don't spend "too long" on one problem, and stay away from problems that look like they test something you don't know well. +It's the worst feeling in the world to sink a couple hours into a problem before realizing that a different problem would've been easy pickings; we can easily avoid this situation with proper time allocation. You have to use your own judgement in deciding what is likely solvable and what should be quit. Generally, don't spend "too long" on one problem, and stay away from problems that look like they test something you don't know well. ### When you see a solution @@ -48,4 +52,4 @@ At the Platinum level, I first read everything and then I usually spend the firs ### Benjamin Qi -Do the partials you can; hopefully by then you've figured out the full solution. :D
\ No newline at end of file +Do the partials you can; hopefully by then you've figured out the full solution. :D diff --git a/content/1_Intro/Contests.mdx b/content/1_Intro/Contests.mdx new file mode 100644 index 0000000..77b69f5 --- /dev/null +++ b/content/1_Intro/Contests.mdx @@ -0,0 +1,97 @@ +--- +id: contests +title: Contests +author: Benjamin Qi +description: Contests that I participate in as well as a few tools. +--- + +Make sure to [upsolve](https://en.wiktionary.org/wiki/upsolve) after the contest ends! + +## Contests that I regularly participate in + +<Resources> + <Resource source="CLIST" title="My Profile" url="https://clist.by/coder/bqi343/" starred>extensive list of contests</Resource> + <Resource source="kenkoooo" title="AtCoder Visualizer" url="https://kenkoooo.com/atcoder/#/table/Benq" starred>keep track of which problems you've done</Resource> +</Resources> + + - [AtCoder](https://beta.atcoder.jp/contests/archive) + - probably the highest quality, although difficulty isn't *always* reasonable + - Contests + - Beginner / Regular: 4 or 6 problems, ~100 min + - Grand: 6 problems, 110 - 150 min + - [Codeforces](http://codeforces.com/problemset) + - Contests + - Div 1/2/3/(4?): 5-6 problems (with some variations), 2 to 2.5 hrs + - Archive + - problem quality, difficulty ratings are ok but not always great + - [Topcoder](https://www.topcoder.com/my-dashboard/) + - Div 2, Div 1 + - 75 min coding, 15 min challenge + - Solutions only tested after contest! + - [Don Mills OJ](http://dmoj.ca/) + - at least one contest every month during school year + +## Other Websites + +<Resources> + <Resource title="Kattis" url="https://open.kattis.com/">misc ICPC contests</Resource> + <Resource title="TOKI" url="https://tlx.toki.id/">Indonesian</Resource> + <Resource title="Kickstart" url="https://codingcompetitions.withgoogle.com/kickstart">Feb - Nov</Resource> + <Resource title="HackerEarth" url="http://hackerearth.com/">Monthly "Easy" - quality is not always good</Resource> + <Resource title="CodeChef" url="http://codechef.com/">Monthly Long, Lunchtime, Cookoff - quality is not always good</Resource> + <Resource title="HackerRank" url="https://www.hackerrank.com/dashboard">apparently only "Hack the Interview" contests now</Resource> + <Resource title="CSAcademy" url="https://csacademy.com/contest/archive/">no regular contests anymore, archive is still worth a look</Resource> +</Resources> + +<!-- - Contests + - Div 2: 5 problems, 2 hrs + - Open: 7 problems, 2 hrs + - Archive + - short statements, good editorials --> + +## Onsite Finals (Individual) + +Let me know if there's something else I should try to qualify for! + +<Resources> + <Resource source="Google" title="Code Jam" url="https://codingcompetitions.withgoogle.com/codejam/">25 from R3</Resource> + <Resource source="TopCoder" title="Open" url="https://tco20.topcoder.com/competition-overview/algorithm/algorithm-ways">16; 4 from SRMs, 10 from R4, 2 from wildcard</Resource> + <Resource source="Facebook" title="Hacker Cup" url="https://www.facebook.com/hackercup/">25 from R3</Resource> + <Resource source="AtCoder" title="World Tour Finals" url="https://codeforces.com/blog/entry/56623">8 from AGC</Resource> +</Resources> + +## US High School + +Only considering contests which allow C++ since the others aren't legit. (add more?) + +<Resources> + <Resource source="Virginia Tech" title="HSPC" url="https://icpc.cs.vt.edu/#/hscontest2017">December, online, teams of 3, 5 hours, past problems on <a href="https://icpc.cs.vt.edu/#/hscontest2017">Kattis</a></Resource> + <Resource source="University of Central Florida" title="HSPT" url="https://hspt.ucfprogrammingteam.org/index.php/hspt-online-edition">Decmeber, online, individual, 4 hours</Resource> + <Resource source="Cornell" title="HSPC" url="https://www.cs.cornell.edu/events/cornell-high-school-programming-contest">April, teams of 3, 3 hours, <a href="https://cornell-hspc19.kattis.com/problems">2019 problems</a></Resource> + <Resource source="Montgomery Blair HS" title="mBIT" url="https://mbit.mbhs.edu/">online, teams of 4, 4 hours</Resource> +</Resources> + +## Codeforces Tools + +<Resources> + <Resource title="CF-Predictor" url="https://chrome.google.com/webstore/detail/cf-predictor/ocfloejijfhhkkdmheodbaanephbnfhn" starred>estimate rating changes!</Resource> + <Resource title="Stopstalk" url="https://www.stopstalk.com">keep track of friends' progress</Resource> + <Resource title="Code Drills" url="https://recommender.codedrills.io/">problem recommender (haven't used personally) </Resource> + <Resource title="CF Visualizer" url="http://cfviz.netlify.com/compare.html">compare users </Resource> + <Resource title="CF Command Line Tool" url="https://codeforces.com/blog/entry/66552">parse problem statements, submit, etc from command line</Resource> + <Resource title="CF Editor" url="https://codeforces.com/blog/entry/72952">text editor using features from above</Resource> +</Resources> + +<!-- - [CF Enhancer](https://chrome.google.com/webstore/detail/codeforces-enhancer/ocmandagmgmkcplckgnfgaokpgkfenmp) + - no longer works --> + +## Contest Tools + +<Resources> + <Resource title="Wolfram Alpha" url="https://www.wolframalpha.com/" starred> </Resource> + <Resource title="CSAcademy Graph Editor, Geo Visualizer" url="https://csacademy.com/app/graph_editor/" starred> </Resource> + <Resource title="Desmos Grapher" url="https://www.desmos.com/calculator" starred> </Resource> + <Resource title="Diff Checker" url="https://www.diffchecker.com/"> </Resource> + <Resource title="2D Geo Visualizer (C++)" url="https://codeforces.com/blog/entry/70330"></Resource> + <Resource title="OEIS" url="https://oeis.org/">integer sequences</Resource> +</Resources>
\ No newline at end of file diff --git a/content/1_Intro/Data_Types.md b/content/1_Intro/Data_Types.md deleted file mode 100644 index 2750aab..0000000 --- a/content/1_Intro/Data_Types.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -id: data-types -title: Data Types -author: Darren Yao -description: Learn about the basic data types needed for competitive programming. ---- - -## Additional Reading - - - CPH 1.1 - 1.3 - - [PAPC 2.3](http://www.csc.kth.se/~jsannemo/slask/main.pdf) - -## Data Types - -There are several main **data types** that are used in contests: 64-bit integers, floating point numbers, booleans, characters, and strings. Assuming that you are familiar with the language you are using, this should be mostly review. - -It's a good idea to use 64-bit integers (`long long` in C++) instead of 32-bit integers (`int`). 64-bit integers are less likely to have overflow issues, since they can store any number between $-9\,223\,372\,036\,854\,775\,808$ and $9\,223\,372\,036\,854\,775\,807$ which is roughly equal to $\pm 9 \times 10^{18}$. Contest problems are usually set such that the 64-bit integer is sufficient. Of course, you shouldn't do this when time and/or memory limits are tight, which may be the case in higher divisions of USACO. - -**Floating point numbers** are used to store decimal values. It is important to know that floating point numbers are not exact, because the binary architecture of computers can only store decimals to a certain precision. Hence, we should always expect that floating point numbers are slightly off. Contest problems will accommodate this by either asking for the greatest integer less than $10^k$ times the value, or will mark as correct any output that is within a certain $\epsilon$ of the judge's answer. (what's epsilon?) - -**Boolean** variables have two possible states: `true` and `false`. We'll usually use booleans to mark whether a certain process is done, and arrays of booleans to mark which components of an algorithm have finished. - -**Character** variables represent a single Unicode character. They are returned when you access the character at a certain index within a string. Characters are represented using the ASCII standard, which assigns each character to a corresponding integer. This allows us to do arithmetic with them; for example, both `cout << ('f' - 'a');` in C++ and `System.out.print('f' - 'a');` in Java will print `5`. - -**Strings** are stored as an array of characters. You can easily access the character at a certain index and take substrings of the string. String problems on USACO Bronze or Silver generally don't involve any special data structures. diff --git a/content/1_Intro/Data_Types.mdx b/content/1_Intro/Data_Types.mdx new file mode 100644 index 0000000..54e4a78 --- /dev/null +++ b/content/1_Intro/Data_Types.mdx @@ -0,0 +1,45 @@ +--- +id: data-types +title: Data Types +author: Darren Yao +description: Learn about the basic data types needed for competitive programming. +prerequisites: + - running-code +--- + +<Resources> + <Resource source="IUSACO" title="2.2 - Data Types">module is based off this</Resource> + <Resource source="CPH" title="1.1 to 1.3 - Introduction"></Resource> + <Resource source="PAPS" title="2.3 - Variables & Types"></Resource> +</Resources> + +<br /> + +(insert some table?) + +There are several main **data types** that are used in contests: integers, floating point numbers, booleans, characters, and strings. Assuming that you are familiar with the language you are using, this should be mostly review. + +The normal **32-bit integer** data type (`int` in C++ and Java) supports values between $−2\,147\,483\,648$ and $2\,147\,483\,647$, which is roughly equal to $\pm 2 \cdot 10^9$. + +Some problems require you to use **64-bit integers** (`long long` in C++ and `long` in Java) instead of 32-bit integers (`int`). 64-bit integers are less likely to have overflow issues, since they can store any number between $-9\,223\,372\,036\,854\,775\,808$ and $9\,223\,372\,036\,854\,775\,807$ which is roughly equal to $\pm 9 \times 10^{18}$. + +Sometimes (but not always) a USACO problem statement (ex. [Haircut](http://www.usaco.org/index.php?page=viewproblem2&cpid=1041)) will contain a warning such as the following: + +> Note that the large size of integers involved in this problem may require the use of 64-bit integer data types (e.g., a "long long" in C/C++). + +Contest problems are usually set such that the 64-bit integer is sufficient, so it might be a good idea to use 64-bit integers in place of 32-bit integers everywhere. Of course, you shouldn't do this when time and/or memory limits are tight, which may be the case in higher divisions of USACO. Also note that in Java, you will need to cast `long` back to `int` when accessing array indices. + +**Floating point numbers** are used to store decimal values. It is important to know that floating point numbers are not exact, because the binary architecture of computers can only store decimals to a certain precision. Hence, we should always expect that floating point numbers are slightly off, so it's generally a bad idea to compare two floating-point numbers for exact equality (`==`). + +Contest problems will usually accommodate the inaccuracy of floating point numbers by checking if the **absolute** or **relative** difference between your output and the answer is less than some small constant like $\epsilon=10^{-9}$. + +- If your output is $x$ and the answer is $y$, the absolute difference is $|x-y|$. +- If your output is $x$ and the answer is $y$, the relative difference is $\frac{|x-y|}{|y|}$. + +This is not the case for USACO, where problems generally have a unique correct output. So when floating point is necessary, the output format will be something along the lines of "Print $10^6$ times the maximum probability of receiving exactly one accepted invitation, rounded down to the nearest integer." (ex. [Cow Dating](http://www.usaco.org/index.php?page=viewproblem2&cpid=924)). + +**Boolean** variables have two possible states: `true` and `false`. We'll usually use booleans to mark whether a certain process is done, and arrays of booleans to mark which components of an algorithm have finished. + +**Character** variables represent a single Unicode character. They are returned when you access the character at a certain index within a string. Characters are represented using the ASCII standard, which assigns each character to a corresponding integer. This allows us to do arithmetic with them; for example, both `cout << ('f' - 'a');` in C++ and `System.out.print('f' - 'a');` in Java will print `5`. + +**Strings** are stored as an array of characters. You can easily access the character at a certain index and take substrings of the string. String problems on USACO Bronze or Silver generally don't involve any special data structures, and can be solved using relatively elementary methods. diff --git a/content/1_Intro/Debugging.mdx b/content/1_Intro/Debugging.mdx new file mode 100644 index 0000000..95ce8b6 --- /dev/null +++ b/content/1_Intro/Debugging.mdx @@ -0,0 +1,383 @@ +--- +id: debugging +title: Debugging +author: Benjamin Qi, Aaron Chew +description: "Identifying errors within your program and how to avoid them in the first place." +--- + +## Within Your Program + +<LanguageSection> + +<CPPSection> + +<Resources> + <Resource + source="Errichto" + title="Video - How to test your solution" + url="https://www.youtube.com/watch?v=JXTVOyQpSGM" + starred + > + using a script to stress test + </Resource> + <Resource + source="Errichto" + title="Asking for help FAQ" + url="https://codeforces.com/blog/entry/64993" + starred + > + some parts from above video + </Resource> +</Resources> + +### Style + +<Resources> + <Resource + source="CF" + title="Competitive C++ Manifesto: A Style Guide" + url="blog/entry/64218" + starred + > + {' '} + don't agree with everything but important to read nonetheless + </Resource> +</Resources> + +### Assertions & Warnings + +<Resources> + <Resource + title="Asserts" + source="LearnCpp" + url="https://www.learncpp.com/cpp-tutorial/7-12a-assert-and-static_assert/" + starred + > + includes static_assert and #define NDEBUG + </Resource> + <Resource title="Assertions" source="GFG" url="assertions-cc"> + subset of above + </Resource> + <Resource + title="Diagnostics" + source="GNU" + url="https://gcc.gnu.org/onlinedocs/cpp/Diagnostics.html" + starred + > + #warning, #error + </Resource> +</Resources> + +### Printing Variables + +Although not feasible if you have to write all code from scratch, [this template](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/CppIO.h) is very helpful for simplifying input / output / debug output. Note that `dbg()` only produces debug output when `-DLOCAL` is included as part of the compilation command, so you don't need to comment out those lines before submitting. + +[Examples - Debug Output](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/CppIO_test.cpp) + +## Compiling + +I use the following to compile and run. + +``` +co() { g++ -std=c++17 -O2 -o $1 $1.cpp -Wall -Wextra -Wshadow -DLOCAL -Wl,-stack_size -Wl,0xF0000000; } +run() { co $1 && ./$1 & fg; } +``` + +### Mac + +According to [this comment](https://codeforces.com/blog/entry/60999?#comment-449312), `-Wl,-stack_size -Wl,0xF0000000` increases the stack size on Mac. Without it, you might get a runtime error if you have many levels of recursion. This matters particularly for contests such as Facebook Hacker Cup where you submit the output of a program you run locally. + +(Documentation?) + +### [Warning Options](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html) + +Includes `-Wall -Wextra -Wshadow` + +[Variable shadowing](https://en.wikipedia.org/wiki/Variable_shadowing) should be avoided whenever possible. + +<Resources> + <Resource source="CF" title="andreyv - Catching Mistakes with GCC" url="https://codeforces.com/blog/entry/15547">more options</Resource> +</Resources> + +### Other Options + +<Warning> + +Ben - I don't use these because they can significantly slow down compilation time and I don't find these messages particularly helpful (But maybe I'm wrong? I'm not so familiar with these.) + +</Warning> + +In Errichto's blog he says that he uses the following as part of his compilation command: + + +``` +-fsanitize=undefined -fsanitize=address -D_GLIBCXX_DEBUG -g +``` + +Let's demonstrate what each of these do with the following program `prog.cpp`, which gives a segmentation fault. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + vector<int> v; + cout << v[-1]; +} +``` + +`g++ prog.cpp -o prog -fsanitize=undefined && ./prog` produces: + +``` +/usr/local/Cellar/gcc/9.2.0_1/include/c++/9.2.0/bits/stl_vector.h:1043:34: runtime error: pointer index expression with base 0x000000000000 overflowed to 0xfffffffffffffffc +zsh: segmentation fault ./prog +``` + +`g++ prog.cpp -o prog -fsanitize=address && ./prog` produces: + +<Spoiler title="AddressSanitizer"> + +``` +AddressSanitizer:DEADLYSIGNAL +================================================================= +==31035==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000106ac6326 bp 0x7ffee913aaa0 sp 0x7ffee913aa20 T0) +==31035==The signal is caused by a READ memory access. +==31035==Hint: address points to the zero page. + #0 0x106ac6325 in main (prog:x86_64+0x100001325) + #1 0x7fff72208cc8 in start (libdyld.dylib:x86_64+0x1acc8) + +==31035==Register values: +rax = 0xfffffffffffffffc rbx = 0x00007ffee913aa20 rcx = 0xfffffffffffffffc rdx = 0x20000fffffffffff +rdi = 0x00007ffee913aa40 rsi = 0x1fffffffffffffff rbp = 0x00007ffee913aaa0 rsp = 0x00007ffee913aa20 + r8 = 0x0000000000000000 r9 = 0x0000000000000000 r10 = 0x0000000000000000 r11 = 0x0000000000000000 +r12 = 0x00000fffdd227544 r13 = 0x00007ffee913aa80 r14 = 0x00007ffee913aa20 r15 = 0x0000000000000000 +AddressSanitizer can not provide additional info. +SUMMARY: AddressSanitizer: SEGV (prog:x86_64+0x100001325) in main +==31035==ABORTING +zsh: abort ./prog +``` + +</Spoiler> + +Finally, `g++ prog.cpp -o prog -D_GLIBCXX_DEBUG -g && ./prog` produces: + +<Spoiler title="Debug"> + +``` +/usr/local/Cellar/gcc/9.2.0_1/include/c++/9.2.0/debug/vector:427: +In function: + std::__debug::vector<_Tp, _Allocator>::reference + std::__debug::vector<_Tp, + _Allocator>::operator[](std::__debug::vector<_Tp, + _Allocator>::size_type) [with _Tp = int; _Allocator = + std::allocator<int>; std::__debug::vector<_Tp, _Allocator>::reference = + int&; std::__debug::vector<_Tp, _Allocator>::size_type = long unsigned + int] + +Error: attempt to subscript container with out-of-bounds index -1, but +container only holds 0 elements. + +Objects involved in the operation: + sequence "this" @ 0x0x7ffee2503a50 { + type = std::__debug::vector<int, std::allocator<int> >; + } +zsh: abort ./prog +``` + +</Spoiler> + +Another example with `prog.cpp` as the following: + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + int v[5]; + cout << v[5]; +} +``` + +`g++ prog.cpp -o prog -fsanitize=undefined && ./prog` produces: + +``` +prog.cpp:6:13: runtime error: index 5 out of bounds for type 'int [5]' +prog.cpp:6:13: runtime error: load of address 0x7ffee0a77a94 with insufficient space for an object of type 'int' +0x7ffee0a77a94: note: pointer points here + b0 7a a7 e0 fe 7f 00 00 25 b0 a5 0f 01 00 00 00 b0 7a a7 e0 fe 7f 00 00 c9 8c 20 72 ff 7f 00 00 + ^ +32766% +``` + +`g++ prog.cpp -o prog -fsanitize=address && ./prog` produces: + +<Spoiler title="AddressSanitizer"> + +``` +================================================================= +==31227==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffeef0e4a54 at pc 0x000100b1bce5 bp 0x7ffeef0e4a10 sp 0x7ffeef0e4a08 +READ of size 4 at 0x7ffeef0e4a54 thread T0 + #0 0x100b1bce4 in main (prog:x86_64+0x100000ce4) + #1 0x7fff72208cc8 in start (libdyld.dylib:x86_64+0x1acc8) + +Address 0x7ffeef0e4a54 is located in stack of thread T0 at offset 52 in frame + #0 0x100b1bc35 in main (prog:x86_64+0x100000c35) + + This frame has 1 object(s): + [32, 52) 'v' (line 5) <== Memory access at offset 52 overflows this variable +HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork + (longjmp and C++ exceptions *are* supported) +SUMMARY: AddressSanitizer: stack-buffer-overflow (prog:x86_64+0x100000ce4) in main +Shadow bytes around the buggy address: + 0x1fffdde1c8f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x1fffdde1c940: 00 00 00 00 f1 f1 f1 f1 00 00[04]f3 f3 f3 f3 f3 + 0x1fffdde1c950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x1fffdde1c990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Shadow byte legend (one shadow byte represents 8 application bytes): + Addressable: 00 + Partially addressable: 01 02 03 04 05 06 07 + Heap left redzone: fa + Freed heap region: fd + Stack left redzone: f1 + Stack mid redzone: f2 + Stack right redzone: f3 + Stack after return: f5 + Stack use after scope: f8 + Global redzone: f9 + Global init order: f6 + Poisoned by user: f7 + Container overflow: fc + Array cookie: ac + Intra object redzone: bb + ASan internal: fe + Left alloca redzone: ca + Right alloca redzone: cb + Shadow gap: cc +==31227==ABORTING +zsh: abort ./prog +``` + +</Spoiler> + +## Running (on Mac) + +According to [StackOverflow](https://stackoverflow.com/a/60516966/5834770) the `& fg` is necessary for getting `zsh` on Mac to display crash messages (such as segmentation fault). For example, consider the running the first `prog.cpp` above with `run prog`. + +If `& fg` is removed from the run command above then the terminal displays no message at all. Leaving it in produces the following (ignore the first two lines): + +``` +[2] 30594 +[2] - running ./$1 +zsh: segmentation fault ./$1 +``` + +### Measuring Time & Memory Usage + +- [CF Comment](https://codeforces.com/blog/entry/49371?#comment-333749) +- [time -v on Mac](https://stackoverflow.com/questions/32515381/mac-os-x-usr-bin-time-verbose-flag) + - use `gtime` + +For example, suppose that `prog.cpp` consists of the following: + +```cpp +#include <bits/stdc++.h> +using namespace std; + +const int BIG = 1e7; +int a[BIG]; + +int main() { + int sum = 0; + for (int i = 0; i < BIG; ++i) sum += a[i]; + cout << sum; +} +``` + +Then `co prog && gtime -v ./prog` gives the following: + +``` + Command being timed: "./prog" + User time (seconds): 0.01 + System time (seconds): 0.01 + Percent of CPU this job got: 11% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.22 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 40216 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 91 + Minor (reclaiming a frame) page faults: 10088 + Voluntary context switches: 3 + Involuntary context switches: 38 + Swaps: 0 + File system inputs: 0 + File system outputs: 0 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 +``` + +Note that $10^7$ integers require $4\cdot 10^7\cdot 10^{-3}\approx 40000$ kilobytes of memory, which is close to $40216$ in the above output as expected. + +</CPPSection> + +</LanguageSection> + +## Stress Testing + +See Errichto's video for details. + +<LanguageSection> + +<CPPSection> + +You can use a [simple script](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/stress.sh) to test two solutions against each other. + +</CPPSection> + +</LanguageSection> + +## Debuggers + +Using a debugger varies from language to language and even IDE to different IDE. For now I will describe the basic operations of a debugger. + +A debugger allows you to pause a code in its execution and see the values as a given point in the debugger. + +To do this, set a "breakpoint" at a certain line of code. When the code runs to that breakpoint, it will pause and you will be able to inspect all the different variables at that certain instance. + +There are two more useful and common operations. Once you are at the breakpoint, you may want to see what happens after the current line is executed. This would be the "Step Over" button that will allow you to move to the next line. Say you are at a line with the following code: `dfs(0,-1)`, if you click "step over" the debugger will ignore showing you what happens in this function and go to the next line. If you click "step in," however, you will enter the function and be able to step through that function. + +In essense, a debugger is a tool to "trace code" for you. It is not much different from just printing the values out at various points in your program. + +Pros of using a debugger: + +- No need to write print statements so you save time +- You can step through the code in real time + +Cons of using a debugger: + +- You cannot see the overall "output" of your program at each stage. For example, if I wanted to see every single value of `i` in the program, I could not using a debugger. +- Most advanced competitive programmers do not use debuggers; it is quite time inefficient. + +<LanguageSection> + +<CPPSection> + +(gdb, valgrind?) + +<IncompleteSection /> + +</CPPSection> + +</LanguageSection> diff --git a/content/1_Intro/Error.png b/content/1_Intro/Error.png Binary files differnew file mode 100644 index 0000000..92ee963 --- /dev/null +++ b/content/1_Intro/Error.png diff --git a/content/1_Intro/Example_Problem.mdx b/content/1_Intro/Example_Problem.mdx deleted file mode 100644 index 57b1e37..0000000 --- a/content/1_Intro/Example_Problem.mdx +++ /dev/null @@ -1,206 +0,0 @@ ---- -id: ex-prob -title: An Example Problem -author: Nathan Wang, Benjamin Qi, Darren Yao -prerequisites: - - Intro - Data Types - - Intro - Input & Output -description: Solutions for "USACO Bronze - Fence Painting" in multiple languages. ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("Bronze", "Promotion Counting", "591", "Intro"), - new Problem("Bronze", "Word Processor", "987", "Intro"), - new Problem("Bronze", "Square Pasture", "663", "Intro"), - new Problem("Bronze", "Bucket Brigade", "939", "Intro"), - ] - } -}; - -[Technical Specifications for USACO Contests](http://www.usaco.org/index.php?page=instructions) - -## Example: [Fence Painting](http://usaco.org/index.php?page=viewproblem2&cpid=567) - -USACO will automatically add a newline to the end of your file if it does not end with one. Make sure not to output trailing spaces! - -### C++ - -[Here](https://www.geeksforgeeks.org/bitsstdc-h-c/) is some info about `<bits/stdc++.h\>` if you are not familiar with it. - -#### Method 1 - -Use [freopen](http://www.cplusplus.com/reference/cstdio/freopen/). If you comment out both of the lines containing `freopen` then the program reads from standard in and writes to standard out as usual. - -```cpp -#include <bits/stdc++.h> -using namespace std; - -int main() { - freopen("paint.in","r",stdin); // reuse standard in to read from "paint.in" - freopen("paint.out","w",stdout); // reuse standard out to write to "paint.out" - vector<bool> cover(100); - int a, b, c, d; cin >> a >> b >> c >> d; - for (int i = a; i < b; ++i) cover[i] = 1; - for (int i = c; i < d; ++i) cover[i] = 1; - int ans = 0; - for (int i = 0; i < 100; ++i) ans += cover[i]; - cout << ans; - // cout << ans << endl; is OK - // cout << ans << "\n"; is OK - // cout << ans << " "; is NOT OK - // cout << ans << "\n\n"; is NOT OK -} -``` - -<optional-content title="Faster Input & Output"> -(using scanf, printf instead?) - -(not using endl?) - -Including `ios_base::sync_with_stdio(0); cin.tie(0);` in the main function can speed up input & output significantly on some tasks. See [here](https://codeforces.com/blog/entry/5217) and [StackOverflow](https://stackoverflow.com/questions/31162367/significance-of-ios-basesync-with-stdiofalse-cin-tienull) for more information. - - - Actually, the former link says that it is supposedly prohibited to use `freopen` to redirect `cin` and `cout` if `ios_base::sync_with_stdio(0); cin.tie(0);` is included, but it works properly as far as I know. - -</optional-content> - -#### Method 2 - -Use [ifstream & ofstream](http://www.cplusplus.com/doc/tutorial/files/). - -```cpp -#include <bits/stdc++.h> -using namespace std; - -int main() { - ifstream fin("paint.in"); - ofstream fout("paint.out"); - vector<bool> cover(100); - int a, b, c, d; fin >> a >> b >> c >> d; - for (int i = a; i < b; ++i) cover[i] = 1; - for (int i = c; i < d; ++i) cover[i] = 1; - int ans = 0; - for (int i = 0; i < 100; ++i) ans += cover[i]; - fout << ans; -} -``` - -### Java - -```java -import java.io.*; -import java.util.*; -public class paintSol { // must be declared in paintSol.java - public static void main(String[] args) throws IOException { - BufferedReader br = new BufferedReader(new FileReader("paint.in")); - PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("paint.out"))); - int[] cover = new int[100]; - - StringTokenizer st = new StringTokenizer(br.readLine()); - int a = Integer.parseInt(st.nextToken()), b = Integer.parseInt(st.nextToken()); - st = new StringTokenizer(br.readLine()); - int c = Integer.parseInt(st.nextToken()), d = Integer.parseInt(st.nextToken()); - - for (int i = a; i < b; i++) cover[i] = 1; - for (int i = c; i < d; i++) cover[i] = 1; - int ans = 0; - for (int i = 0; i < 100; i++) ans += cover[i]; - pw.println(ans); - pw.close(); // make sure to include this line -- flushes the output. - } -} -``` - -Alternatively, we can use the InputReader given in the previous module. - -<spoiler title="InputReader"> - -```java -import java.util.*; -import java.io.*; - -public class template { - - static class InputReader { - BufferedReader reader; - StringTokenizer tokenizer; - - public InputReader() throws FileNotFoundException { - reader = new BufferedReader(new FileReader("paint.in")); - tokenizer = null; - } - - String next() { - while (tokenizer == null || !tokenizer.hasMoreTokens()) { - try { - tokenizer = new StringTokenizer(reader.readLine()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return tokenizer.nextToken(); - } - - public int nextInt() { - return Integer.parseInt(next()); - } - - public long nextLong() { - return Long.parseLong(next()); - } - - public double nextDouble() { - return Double.parseDouble(next()); - } - } - - public static void main(String[] args) throws FileNotFoundException, IOException { - - InputReader r = new InputReader(); - PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("paint.out"))); - - int[] cover = new int[100]; - for (int i = a; i < b; i++) cover[i] = 1; - for (int i = c; i < d; i++) cover[i] = 1; - int ans = 0; - for (int i = 0; i < 100; i++) ans += cover[i]; - - pw.println(ans); - pw.close(); // flush output - } -} -``` - -</spoiler> - -### Python 3 - -See [here](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files) for documentation about file I/O. - -```py -fin = open("paint.in","r") -fout = open("paint.out","w") -cover = [0 for i in range(100)] -a,b = map(int,fin.readline().split()) -c,d = map(int,fin.readline().split()) -for i in range(a,b): - cover[i] = 1 -for i in range(c,d): - cover[i] = 1 -ans = 0 -for i in range(100): - ans += cover[i] -fout.write(str(ans)) -``` - -## Introductory Problems - -The following require relatively little programming experience and no algorithmic knowledge. Do as many as you want, then move on! You do not have to do all of them. - -<problems-list problems={metadata.problems.general} /> - - -Also check the [CSES Introductory Problems](https://cses.fi/problemset/list/) up to and including "Palindrome Reorder."
\ No newline at end of file diff --git a/content/1_Intro/Expected.mdx b/content/1_Intro/Expected.mdx new file mode 100644 index 0000000..28c77b3 --- /dev/null +++ b/content/1_Intro/Expected.mdx @@ -0,0 +1,62 @@ +--- +id: expected +title: Expected Knowledge +author: Nathan Wang, Benjamin Qi +description: What you're expected to know before continuing onto the rest of USACO Bronze. +prerequisites: + - io +--- + +import { Problem } from "../models"; + +export const problems = { + general: [ + new Problem("Bronze", "Promotion Counting", "591", "Very Easy"), + new Problem("Bronze", "Teleportation", "807", "Very Easy"), + new Problem("Bronze", "Square Pasture", "663", "Very Easy"), + new Problem("Bronze", "Word Processor", "987", "Very Easy"), + new Problem("Bronze", "Bucket Brigade", "939", "Very Easy"), + ] +}; + +The remainder of this guide assumes that you know the basics of how to code in one of the languages listed above, including the following topics: + +- Variables + - Data types +- Reading Input +- Writing Output +- Loops +- If / Else +- Logical operators +- Functions + - Basic Recursion (a function calling itself) +- Arrays + - Multidimensional Arrays + +In particular, contestants using Java should be familiar with roughly the first half of AP Computer Science A. Familiarity with [competition math](https://github.com/bqi343/USACO/blob/master/Resources/Competition%20Math.md) (ex. AIME qualification) is helpful but not required. + +## Getting Started + +You may find the following resources helpful for familiarizing yourself with your language of choice. Resources below (including starred ones) are optional. + +<Resources> + <Resource source="CodeSignal" title="Arcade, Interview Practice" url="https://codesignal.com/" starred>can practice basics</Resource> + <Resource source="Philippines OI" title="Prepare" url="https://noi.ph/prepare/" starred>lots of links!</Resource> + <Resource source="VPlanet" title="Learn to Code" url="https://www.vplanetcoding.com/course1">basic problems, mostly loops</Resource> + <Resource source="IOI" title="Getting Started" url="https://ioinformatics.org/page/getting-started/14">not up to date</Resource> + <Resource source="Quora" title="Joshua Pan - Schedule for Beginners" url="https://www.quora.com/What-is-a-good-schedule-to-follow-for-becoming-better-at-competitive-programming-for-beginners">generally good, although CSES problemset (see "Resources") is definitely a better place to start than USACO Training or Codechef</Resource> +</Resources> + +## Introductory Problems + +The following require relatively little programming experience and no algorithmic knowledge. + +<Problems problems={problems.general} /> + +Also check the [CSES Introductory Problems](https://cses.fi/problemset/list/) up to and including "Palindrome Reorder." Once you're done with these, you should continue onto the rest of Bronze. + +<Warning> + +Some modules in the "Intro" section (ex. Fast Input & Output) are not relevant for USACO Bronze contestants. It is probably best to skip these for now and return to these later. However, make sure to check [Code Conventions](./code-con) for the templates that will be used for code snippets in later modules! + +</Warning> diff --git a/content/1_Intro/Factors_Choosing.mdx b/content/1_Intro/Factors_Choosing.mdx new file mode 100644 index 0000000..ec7f6af --- /dev/null +++ b/content/1_Intro/Factors_Choosing.mdx @@ -0,0 +1,231 @@ +--- +id: factors-choosing +title: "Factors to Consider When Choosing a Language" +author: Benjamin Qi +description: Reasons why choice of language matters significantly outside of USACO Bronze. +prerequisites: + - choosing-lang +--- + +## Notes + + - USACO problemsetters don't always test Java (and rarely Python) solutions when setting constraints. + - Python lacks a data structure that keeps its keys in sorted order (the equivalent of `set` in C++), which is required for some silver problems. + - Java lacks features such as `#define`, `typedef`, and `auto` that are present in C++ (which some contestants rely on extensively, see "Shortening C++"). + +## Time Limit + +Although both Python and Java receive two times the C++ time limit in USACO, this is not the case for most other websites (ex. CodeForces). Even with the extended time limits, Python and Java sometimes have trouble passing. + +<!-- (may need to rewrite these solutions so they are more similar) --> + +Rewriting the C++ solution for [USACO Silver Wormsort](http://www.usaco.org/index.php?page=viewproblem2&cpid=992) in Python receives TLE (Time Limit Exceeded) on 2/10 cases. I'm not sure whether it is possible to pass this problem with Python. + +<Spoiler title="Python3 8/10 Solution"> + +```py +# 8/10 test cases ... + +fin = open("wormsort.in","r") +lines = [line for line in fin] +N,M = map(int,lines[0].split()) +p = list(map(lambda x: int(x)-1,lines[1].split())) + +ed = [] +for i in range(2,len(lines)): + a,b,w = map(int,lines[i].split()) + a -= 1 + b -= 1 + ed.append([w,a,b]) +ed.sort() +ed.reverse() + +adj = [[] for i in range(N)] +vis = [0 for i in range(N)] +cnt = 0 + +def dfs(x): + global cnt + if vis[x] != 0: + return + vis[x] = cnt + for i in adj[x]: + dfs(i) + +def ok(mid): + global cnt + for i in range(N): + vis[i] = 0 + adj[i].clear() + for i in range(mid): + a,b = ed[i][1],ed[i][2] + adj[a].append(b) + adj[b].append(a) + for i in range(N): + if vis[i] == 0: + cnt += 1 + todo = [i] + ind = 0 + while ind < len(todo): + x = todo[ind] + ind += 1 + vis[x] = cnt + for i in adj[x]: + if vis[i] == 0: + vis[i] = -cnt + todo.append(i) + ok = True + for i in range(N): + if vis[i] != vis[p[i]]: + ok = False + return ok + +lo,hi = 0,M +while lo < hi: + mid = (lo+hi)//2 + if ok(mid): + hi = mid + else: + lo = mid+1 + +fout = open("wormsort.out","w") + +fout.write(str(-1 if lo == 0 else ed[lo-1][0])) +fout.write('\n') +``` + +</Spoiler> + +A similar solution in Java requires almost 3s, which is fairly close to the time limit of 4s. + +<Spoiler title="Java Solution"> + +```java +import java.io.*; // from Nick Wu +import java.util.*; +public class wormsort { + public static void main(String[] args) throws IOException{ + BufferedReader br = new BufferedReader(new FileReader("wormsort.in")); + StringTokenizer st = new StringTokenizer(br.readLine()); + int n = Integer.parseInt(st.nextToken()); + int m = Integer.parseInt(st.nextToken()); + loc = new int[n]; + component = new int[n]; + edges = new LinkedList[n]; + for(int i = 0; i < n; i++) edges[i] = new LinkedList<>(); + lhs = new int[m]; + rhs = new int[m]; + weight = new int[m]; + st = new StringTokenizer(br.readLine()); + for(int i = 0; i < n; i++) loc[i] = Integer.parseInt(st.nextToken())-1; + for(int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + lhs[i] = Integer.parseInt(st.nextToken())-1; + rhs[i] = Integer.parseInt(st.nextToken())-1; + weight[i] = Integer.parseInt(st.nextToken()); + } + br.close(); + int minW = 0; + int maxW = 1000000001; + while(minW != maxW) { + int mid = (minW + maxW + 1) / 2; + if(valid(mid)) minW = mid; + else maxW = mid-1; + } + if(minW > 1e9) minW = -1; + PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("wormsort.out"))); + pw.println(minW); + pw.close(); + } + static int[] loc, lhs, rhs, weight; + static LinkedList<Integer>[] edges; + static int[] component; + private static void dfs(int curr, int label) { + if(component[curr] == label) return; + component[curr] = label; + for(int child: edges[curr]) dfs(child, label); + } + private static boolean valid(int minW) { + Arrays.fill(component, -1); + for(int i = 0; i < edges.length; i++) edges[i].clear(); + for(int i = 0; i < lhs.length; i++) { + if(weight[i] >= minW) { + edges[lhs[i]].add(rhs[i]); + edges[rhs[i]].add(lhs[i]); + } + } + int numcomps = 0; + for(int i = 0; i < component.length; i++) { + if(component[i] < 0) { + dfs(i, numcomps++); + } + } + for(int i = 0; i < loc.length; i++) { + if(component[i] != component[loc[i]]) return false; + } + return true; + } +} +``` + +</Spoiler> + +A comparable C++ solution runs in less than 700ms. + +<Spoiler title="C++ Solution"> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +typedef vector<int> vi; + +const int MX = 1e5+5; + +int loc[MX], comp[MX], lhs[MX], rhs[MX], wei[MX]; +vi adj[MX]; +int n,m; + +void dfs(int cur, int label) { + if (comp[cur] == label) return; + comp[cur] = label; + for (int c: adj[cur]) dfs(c,label); +} + +bool valid(int minW) { + for (int i = 0; i < n; ++i) { + comp[i] = -1; + adj[i].clear(); + } + for (int i = 0; i < m; ++i) if (wei[i] >= minW) + adj[lhs[i]].push_back(rhs[i]), adj[rhs[i]].push_back(lhs[i]); + int numComps = 0; + for (int i = 0; i < n; ++i) if (comp[i] < 0) + dfs(i,numComps++); + for (int i = 0; i < n; ++i) + if (comp[i] != comp[loc[i]]) return 0; + return 1; +} + +int main() { + ios_base::sync_with_stdio(0); cin.tie(0); + freopen("wormsort.in","r",stdin); + freopen("wormsort.out","w",stdout); + cin >> n >> m; + for (int i = 0; i < n; ++i) cin >> loc[i], loc[i] --; + for (int i = 0; i < m; ++i) { + cin >> lhs[i], lhs[i] --; + cin >> rhs[i], rhs[i] --; + cin >> wei[i]; + } + int minW = 0, maxW = (int)1e9+1; + while (minW != maxW) { + int mid = (minW+maxW+1)/2; + if (valid(mid)) minW = mid; + else maxW = mid-1; + } + if (minW > 1e9) minW = -1; + cout << minW; +} +``` +</Spoiler> diff --git a/content/1_Intro/Fast_IO.mdx b/content/1_Intro/Fast_IO.mdx new file mode 100644 index 0000000..3b5f351 --- /dev/null +++ b/content/1_Intro/Fast_IO.mdx @@ -0,0 +1,466 @@ +--- +id: fast-io +title: Fast Input & Output +author: Benjamin Qi, Nathan Chen +description: "Speeding up I/O speeds can make a substantial difference in problems with large inputs." +--- + +Generally, input and output speed isn't an issue. However, some platinum tasks have relatively large input files. The [USACO Instructions Page](http://www.usaco.org/index.php?page=instructions) briefly mentions some ways of speeding up I/O; let's check that these actually make a difference. + +## Fast Input + +The largest USACO input file we know of is test case 11 of [USACO Platinum - Robotic Cow Herd](http://www.usaco.org/index.php?page=viewproblem2&cpid=674) (10.3 megabytes). The answer to this test case is $10^{18}$ (with $N=K=10^5$ and all microcontrollers costing $10^8$). + +<LanguageSection> + +<CPPSection> + +### Method 1: `freopen` with `cin` + +The slowest method. + +<Spoiler title="973ms"> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +vector<int> P[100000]; + +int main() { + freopen("roboherd.in","r",stdin); + freopen("roboherd.out","w",stdout); + int N,K; cin >> N >> K; + for (int i = 0; i < N; ++i) { + int M; cin >> M; P[i].resize(M); + for (int j = 0; j < M; ++j) cin >> P[i][j]; + } + if (N == 3) cout << 61; + else cout << 1000000000000000000; +} +``` + +</Spoiler> + +### Method 2: `freopen` with `scanf` + +Significantly faster. + +<Spoiler title="281ms"> + +```cpp +#include <bits/stdc++.h> // 281 ms +using namespace std; + +vector<int> P[100000]; +int main() { + freopen("roboherd.in","r",stdin); + freopen("roboherd.out","w",stdout); + int N,K; scanf("%d%d",&N,&K); + for (int i = 0; i < N; ++i) { + int M; scanf("%d",&M); P[i].resize(M); + for (int j = 0; j < M; ++j) scanf("%d",&P[i][j]); + } + if (N == 3) printf("%d",61); + else printf("%lld",1000000000000000000LL); +} +``` + +</Spoiler> + +### Method 3: `ifstream` and `ofstream` + +About as fast. + +<Spoiler title="258ms"> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +vector<int> P[100000]; + +int main() { // 258 ms + ifstream fin("roboherd.in"); + ofstream fout("roboherd.out"); + int N,K; fin >> N >> K; + for (int i = 0; i < N; ++i) { + int M; fin >> M; P[i].resize(M); + for (int j = 0; j < M; ++j) fin >> P[i][j]; + } + if (N == 3) fout << 61; + else fout << 1000000000000000000; +} +``` + +</Spoiler> + +### A Faster Method + +I we use FastIO from [here](https://github.com/bqi343/USACO/blob/master/Implementations/content/various/FastIO.h) then the runtime is further reduced. + +<Spoiler title="91ms"> + +```cpp +using namespace FastIO; + +vector<int> P[100000]; + +int main() { + freopen("roboherd.in","r",stdin); + freopen("roboherd.out","w",stdout); + int N,K; ri(N,K); + for (int i = 0; i < N; ++i) { + int M; ri(M); P[i].resize(M); + for (int j = 0; j < M; ++j) ri(P[i][j]); + } + if (N == 3) cout << 61; + else cout << 1000000000000000000; +} +``` + +</Spoiler> + +### Significance of `ios_base::sync_with_stdio(0); cin.tie(0);` + +Adding this line as the first line of `main()` reduced the runtime of the first method to 254 ms. + +<Resources> + <Resource + source="CF" + url="blog/entry/5217" + title="Yet again on C++ I/O" + starred + > + timing various I/O methods + </Resource> + <Resource + source="SO" + url="https://stackoverflow.com/questions/31162367/significance-of-ios-basesync-with-stdiofalse-cin-tienull" + title="Significance of [above]" + > + {' '} + </Resource> + <Resource source="CPP" url="http://www.cplusplus.com/reference/ios/ios_base/sync_with_stdio/" title="ios_base::sync_with_stdio"> + documentation + </Resource> + <Resource source="CPP" url="http://www.cplusplus.com/reference/ios/ios/tie/" title="ios::tie"> + documentation + </Resource> +</Resources> + +You may see `cin.sync_with_stdio(0);` used in place of `ios_base::sync_with_stdio(0);` but they should have the same effect. + +Actually, the first link says that it is supposedly prohibited to use `freopen` to redirect `cin` and `cout` if `ios_base::sync_with_stdio(0); cin.tie(0);` is included, but it works properly as far as I know. + +</CPPSection> + +<JavaSection> + +Keep in mind that a Java program that only reads in $N$ and outputs a number takes 138ms on USACO. + +[`Scanner`](https://docs.oracle.com/javase/8/docs/api/java/util/Scanner.html) is probably the easiest way to read input in Java, though it is also extremely slow. + +<Spoiler title="3188ms"> + +```java +import java.util.*; +import java.io.*; + +//3188ms +public class roboherd_scanner { + + static int P[][] = new int[100000][]; + + public static void main(String[] args) throws Exception { + Scanner sc = new Scanner(new File("roboherd.in")); + PrintWriter pw = new PrintWriter(new FileWriter("roboherd.out")); + + int N = sc.nextInt(); + int K = sc.nextInt(); + for(int i = 0; i < N; ++i) { + int M = sc.nextInt(); P[i] = new int[M]; + for(int j = 0; j < M; ++j) P[i][j] = sc.nextInt(); + } + + if(N == 3) pw.println(61); + else pw.println(1000000000000000000L); + pw.close(); + } +} +``` + +</Spoiler> + +A common alternative to reading input for programming contests is [`BufferedReader`](https://docs.oracle.com/javase/8/docs/api/java/io/BufferedReader.html), which reads faster from a file than `Scanner`. `BufferedReader` reads line-by-line with the `.readLine()` method, which returns a string. Methods like `Integer.parseInt()` are used to convert strings into primitives, and they can directly convert a line into a number, like in `Integer.parseInt(br.readLine())`. + +Reading input is more complicated when multiple, space-separated values are placed in a single line. In order to individually read the values in each line, the programmer usually uses the `.split()` method in `String` or the `.nextToken()` in [`StringTokenizer`](https://docs.oracle.com/javase/8/docs/api/java/util/StringTokenizer.html). Notice that `StringTokenizer` for splitting strings is slightly faster than `.split()`. Apparently [`StreamTokenizer`](https://docs.oracle.com/javase/8/docs/api/java/io/StreamTokenizer.html) is even faster! + +_Reading input with `BufferedReader` and `.split()`_: + +<Spoiler title="1209ms"> + +```java +import java.util.*; +import java.io.*; + +//1209ms +public class roboherd_buffered_reader_string_split { + + static int P[][] = new int[100000][]; + + public static void main(String[] args) throws Exception { + BufferedReader br = new BufferedReader(new FileReader("roboherd.in")); + PrintWriter pw = new PrintWriter(new FileWriter("roboherd.out")); + + String[] tokens = br.readLine().split(" "); + int N = Integer.parseInt(tokens[0]); + int K = Integer.parseInt(tokens[1]); + for(int i = 0; i < N; ++i) { + tokens = br.readLine().split(" "); + int M = Integer.parseInt(tokens[0]); P[i] = new int[M]; + for(int j = 0; j < M; ++j) P[i][j] = Integer.parseInt(tokens[j+1]); + } + + if(N == 3) pw.println(61); + else pw.println(1000000000000000000L); + pw.close(); + } +} +``` + +</Spoiler> + +_Reading input with `BufferedReader` and `StringTokenizer`_: + +<Spoiler title="986ms"> + +```java +import java.util.*; +import java.io.*; + +//986ms +public class roboherd_buffered_reader_string_tokenizer { + + static int P[][] = new int[100000][]; + + public static void main(String[] args) throws Exception { + BufferedReader br = new BufferedReader(new FileReader("roboherd.in")); + PrintWriter pw = new PrintWriter(new FileWriter("roboherd.out"))); + + StringTokenizer st = new StringTokenizer(br.readLine()); + int N = Integer.parseInt(st.nextToken()); + int K = Integer.parseInt(st.nextToken()); + for(int i = 0; i < N; ++i) { + st = new StringTokenizer(br.readLine()); + int M = Integer.parseInt(st.nextToken()); P[i] = new int[M]; + for(int j = 0; j < M; ++j) P[i][j] = Integer.parseInt(st.nextToken()); + } + + if(N == 3) pw.println(61); + else pw.println(1000000000000000000L); + pw.close(); + } +} +``` + +</Spoiler> + +_Reading input with `BufferedReader` and `StreamTokenizer`_: + +<Spoiler title="569ms"> + +```java +import java.util.*; +import java.io.*; + +public class roboherd { + static int P[][] = new int[100000][]; + static StreamTokenizer in; + static int nextInt() throws IOException{ + in.nextToken(); + return (int)in.nval; + } + public static void main(String[] args) throws Exception { + in = new StreamTokenizer(new BufferedReader(new FileReader("roboherd.in"))); + PrintWriter pw = new PrintWriter(new FileWriter("roboherd.out")); + + int N = nextInt(); + int K = nextInt(); + for(int i = 0; i < N; ++i) { + int M = nextInt(); P[i] = new int[M]; + for(int j = 0; j < M; ++j) P[i][j] = nextInt(); + } + + if(N == 3) pw.println(61); + else pw.println(1000000000000000000L); + pw.close(); + } +} +``` + +</Spoiler> + +Faster methods of reading input exist too - Even faster than `BufferedReader` is a custom-written Fast I/O class that uses `InputStream`. Note that this custom class is similar to the custom-written `FastIO.h` in the C++ section, as both read input through a byte buffer. + +<Spoiler title="320ms"> + +```java +import java.util.*; +import java.io.*; + +//320ms +public class roboherd_is { + + static int P[][] = new int[100000][]; + + public static void main(String[] args) throws Exception { + FastIO sc = new FastI("roboherd.in"); + PrintWriter pw = new PrintWriter(new FileWriter("roboherd.out")); + + int N = sc.nextInt(); + int K = sc.nextInt(); + for(int i = 0; i < N; ++i) { + int M = sc.nextInt(); P[i] = new int[M]; + for(int j = 0; j < M; ++j) P[i][j] = sc.nextInt(); + } + + if(N == 3) pw.println(61); + else pw.println(1000000000000000000L); + pw.close(); + } + + // new FastI("file_name") to read a file + // new FastI(System.in) to read from stdin + // has similar syntax to Scanner, though much faster :) + static class FastI { + + InputStream dis; + byte[] buffer = new byte[1 << 17]; + int pointer = 0; + + public FastIO(String fileName) throws Exception { + dis = new FileInputStream(fileName); + } + + public FastIO(InputStream is) throws Exception { + dis = is; + } + + int nextInt() throws Exception { + int ret = 0; + + byte b; + do { + b = nextByte(); + } while (b <= ' '); + boolean negative = false; + if (b == '-') { + negative = true; + b = nextByte(); + } + while (b >= '0' && b <= '9') { + ret = 10 * ret + b - '0'; + b = nextByte(); + } + + return (negative) ? -ret : ret; + } + + long nextLong() throws Exception { + long ret = 0; + + byte b; + do { + b = nextByte(); + } while (b <= ' '); + boolean negative = false; + if (b == '-') { + negative = true; + b = nextByte(); + } + while (b >= '0' && b <= '9') { + ret = 10 * ret + b - '0'; + b = nextByte(); + } + + return (negative) ? -ret : ret; + } + + byte nextByte() throws Exception { + if (pointer == buffer.length) { + dis.read(buffer, 0, buffer.length); + pointer = 0; + } + return buffer[pointer++]; + } + + String next() throws Exception { + StringBuffer ret = new StringBuffer(); + + byte b; + do { + b = nextByte(); + } while (b <= ' '); + while (b > ' ') { + ret.appendCodePoint(b); + b = nextByte(); + } + + return ret.toString(); + } + + } +} +``` + +</Spoiler> + +The most realistic input method to implement in contest is `BufferedReader`, as it is relatively fast for the amount of code necessary. + +</JavaSection> + +<PySection> + +Faster than the first C++ method! Significantly less if $P$ does not need to be stored. + +<Spoiler title="853ms"> + +```py +fin = open("roboherd.in","r") +fout = open("roboherd.out","w") +N,K = map(int,fin.readline().split()) +P = [[] for i in range(N)] +for i in range(N): + P[i] = map(int,fin.readline().split()) +if N == 3: + fout.write(str(61)) +else: + fout.write(str(1000000000000000000)) +``` + +</Spoiler> + +</PySection> + +</LanguageSection> + +## Fast Output + +In general, it may be faster to store the answer all in a single `string` (C++) or `StringBuffer` (Java) and outputting it with a single function call. This method avoids the overhead of calling an output method many times, especially if the output is generated in many parts. + +<LanguageSection> + +<CPPSection> + +The CF blog mentioned above notes that when printing many lines in C++, it may be faster to use the newline character `\n` in place of `endl`. Output streams in C++ (such as `cout` and `ofstream`) are buffered, meaning that they don't immediately print their output, but store some of it. At some point, the buffer's contents are written (i.e. "flushed") to the output device (e.g the standard output stream or a file). Buffering the output helps with efficiency if accessing the output device (like a file) is slow. Because `endl` flushes the output, it may be faster to use `\n` instead and avoid unnecessary flushes. + +</CPPSection> + +<JavaSection> + +When printing to the standard output stream in Java, it is faster to use `new PrintWriter(System.out)` than `System.out`. The syntax for `PrintWriter` is equivalent to that of `System.out` so it should be easy to use. The only difference is that one has to call `.flush()` or `.close()` on the `PrintWriter` at the very end of the program in order to write the output. + +</JavaSection> + +</LanguageSection> diff --git a/content/1_Intro/Input_Output.md b/content/1_Intro/Input_Output.md deleted file mode 100644 index fa03cff..0000000 --- a/content/1_Intro/Input_Output.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -id: io -title: Input & Output -author: Darren Yao -description: Demonstrates how to read input and print output for USACO contests. ---- - -## Additional Reading - - - CSES 1.2 - - [PAPC 2.4](http://www.csc.kth.se/~jsannemo/slask/main.pdf) - -## Standard I/O - -In most websites (such as CodeForces and CSES), input and output are **standard**. - -### C++ - -The [<iostream\>](http://www.cplusplus.com/reference/iostream/) library suffices. - -```cpp -#include <iostream> -using namespace std; - -int main() { - int x; cin >> x; - cout << "FOUND " << x << "\n"; -} -``` - -### Java - -In your CS classes, you've probably implemented input and output using standard input and standard output, or using [`Scanner`](https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html) to read input and `System.out.print` to print output. These methods work, but `Scanner` and `System.out.print` are slow when we have to handle inputting and outputting tens of thousands of lines. Thus, we use `BufferedReader` and `PrintWriter` instead, which are faster because they buffer the input and output and handle it all at once as opposed to parsing each line individually. - -Here is a Java template for input and output, which is effectively a faster Scanner. We import the entire `util` and `io` libraries for ease of use. - -<spoiler title="Standard I/O"> - -```java -import java.io.*; -import java.util.*; - -public class template { - static class InputReader { - BufferedReader reader; - StringTokenizer tokenizer; - - public InputReader(InputStream stream) { - reader = new BufferedReader(new InputStreamReader(stream), 32768); - tokenizer = null; - } - - String next() { // reads in the next string - while (tokenizer == null || !tokenizer.hasMoreTokens()) { - try { - tokenizer = new StringTokenizer(reader.readLine()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return tokenizer.nextToken(); - } - - public int nextInt() { // reads in the next int - return Integer.parseInt(next()); - } - - public long nextLong() { // reads in the next long - return Long.parseLong(next()); - } - - public double nextDouble() { // reads in the next double - return Double.parseDouble(next()); - } - } - - static InputReader r = new InputReader(System.in); - static PrintWriter pw = new PrintWriter(System.out); - - public static void main(String[] args) { - - // YOUR CODE HERE - - pw.close(); // flushes the output once printing is done - } -} -``` - -</spoiler> - -Here's a brief description of the methods in our `InputReader` class, with an instance `r`, and `PrintWriter` with an instance `pw`. - - -| Method | Description | -| ------------------ | ------------------------------------------------------------------- | -| `r.next()` | Reads the next token (up to a whitespace) and returns a `String` | -| `r.nextInt()` | Reads the next token (up to a whitespace) and returns as an `int` | -| `r.nextLong()` | Reads the next token (up to a whitespace) and returns as a `long` | -| `r.nextDouble()` | Reads the next token (up to a whitespace) and returns as a `double` | -| `pw.println()` | Prints the argument to designated output stream and adds newline | -| `pw.print()` | Prints the argument to designated output stream | - - -## File I/O - -In USACO, input is read from a file called `problemname.in`, and output is printed to a file called `problemname.out`. Note that you'll have to rename the `.in` and `.out` files. For example, for [this problem](http://www.usaco.org/index.php?page=viewproblem2&cpid=1035), you would replace `problemname` with `socdist1` to get `socdist1.in` and `socdist1.out`. - -In order to test a program, create a file called `problemname.in`, and then run the program. The output will be printed to `problemname.out`. - -## C++ - -You will need the [<cstdio\>](http://www.cplusplus.com/reference/cstdio/) or the [<fstream\>](http://www.cplusplus.com/reference/fstream/) library. Essentially, replace every instance of the word `problemname` in the word below with the input/output file name, which should be given in the problem. - -Below, we have included C++ templates for input and output. We use `using namespace std;` so that we don't have to preface standard library functions with `std::` each time we use them. - -If `<cstdio>` is used: - -```cpp -#include <cstdio> -using namespace std; - -int main() { - freopen("problemname.in", "r", stdin); - freopen("problemname.out", "w", stdout); - // rest of your code ... -} -``` - -If `<fstream>` is used: (Note that you cannot use C-style I/O (`scanf`, `printf`) with this method): - -```cpp -#include <fstream> -using namespace std; - -int main() { - ifstream fin("problemname.in"); - ofstream fout("problemname.out"); - // rest of your code ... -} -``` - -## Java - -We can modify the template above to support file I/O. - -<spoiler title="File I/O"> - -```java -import java.util.*; -import java.io.*; - -public class template { - static class InputReader { - BufferedReader reader; - StringTokenizer tokenizer; - - public InputReader() throws FileNotFoundException { - reader = new BufferedReader(new FileReader("template.in")); - tokenizer = null; - } - - String next() { // reads in the next String - while (tokenizer == null || !tokenizer.hasMoreTokens()) { - try { - tokenizer = new StringTokenizer(reader.readLine()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return tokenizer.nextToken(); - } - - public int nextInt() { // reads in the next int - return Integer.parseInt(next()); - } - - public long nextLong() { // reads in the next long - return Long.parseLong(next()); - } - - public double nextDouble() { // reads in the next double - return Double.parseDouble(next()); - } - } - - public static void main(String[] args) throws FileNotFoundException, IOException { - - InputReader r = new InputReader(); - PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("template.out"))); - - // YOUR CODE HERE - - pw.close(); // flushes the output once printing is done - } -} -``` - -</spoiler> - -Here's an example to show how input/output works. Let's say we want to write a program that takes three numbers as input and prints their sum. - -```java -// InputReader template code above -static InputReader r = new InputReader(System.in); -static PrintWriter pw = new PrintWriter(System.out); - -public static void main(String[] args) { - int a = r.nextInt(); - int b = r.nextInt(); - int c = r.nextInt() - pw.println(a + b + c); - pw.close(); -} -``` diff --git a/content/1_Intro/Input_Output.mdx b/content/1_Intro/Input_Output.mdx new file mode 100644 index 0000000..e382d5f --- /dev/null +++ b/content/1_Intro/Input_Output.mdx @@ -0,0 +1,429 @@ +--- +id: io +title: Input & Output +author: Darren Yao, Benjamin Qi, Nathan Wang +description: Demonstrates how to read input and print output for USACO contests, including an example problem. +prerequisites: + - data-types +--- + +import { Problem } from "../models"; + +export const problems = { + cses: [ + new Problem("CSES", "Weird Algorithm", "1068", "Very Easy"), + ], + fence: [ + new Problem("Bronze", "Fence Painting", "567", "Very Easy"), + ], +}; + +<Resources> + <Resource source="IUSACO" title="2.1 - Input and Output">module is based off this</Resource> + <Resource source="CPH" title="1.2 - Input and Output">cin, getline, files</Resource> + <Resource source="PAPS" title="2.4 - Input and Output">cin, getline</Resource> +</Resources> + +<!-- We use `using namespace std;` so that we don't have to preface standard library functions with `std::` each time we use them. --> + +## Standard I/O + +In most websites (such as CodeForces and CSES), input and output are **standard**. + +<Problems problems={problems.cses} /> + +Note that this problem requires **64-bit integers**. + +<LanguageSection> + +<CPPSection> + +### C++ + +The [<iostream\>](http://www.cplusplus.com/reference/iostream/) library suffices. + +```cpp +#include <iostream> +using namespace std; + +int main() { + // the following optional line improves performance (see the "Fast Input / Output" module) + ios_base::sync_with_stdio(false); cin.tie(NULL); + int x; cin >> x; + cout << "FOUND " << x << "\n"; +} +``` + +Alternatively, you can use the [<cstdio\>](http://www.cplusplus.com/reference/cstdio/) library's `scanf` and `printf` functions, which are slightly more complicated to use, but are significantly faster (generally only an issue with large input sizes): + +```cpp +#include <cstdio> +using namespace std; + +int main() { + int x, y; + // %d specifies that a value of type int is being input. + // Use %lld (a few judging platforms might need %I64d) + // to input a long long (64-bit) integer. + // Many other specifiers are also available; see link for more details. + // Be sure to add a & character (address-of operator) when using + // scanf, UNLESS you are inputing a string with %s. + // It is possible to input multiple values at a time as shown below. + scanf("%d%d", &x, &y); + + // Specifiers for printf are mostly the same as those used + // by scanf, with the notable exception of floating-point numbers. + // Use a backslash character followed by the lowercase + // letter n to denote a newline. + // The address-of operator, &, is not used here. + printf("Found %d and %d\n", x, y); +} +``` + +</CPPSection> + +<JavaSection> + +### Java + +In your CS classes, you've probably implemented input and output using standard input and standard output, or using [`Scanner`](https://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html) to read input and `System.out.print` to print output. These methods work, but `Scanner` and `System.out.print` are slow when we have to handle inputting and outputting tens of thousands of lines. Thus, we use `BufferedReader` and `PrintWriter` instead, which are faster because they buffer the input and output and handle it all at once as opposed to parsing each line individually. + +Here is a Java template for input and output, which is effectively a faster Scanner. We import the entire `util` and `io` libraries for ease of use. Note that this **must** be declared within a file named `template.java`. + +```java +import java.io.*; +import java.util.*; + +public class template { + static class InputReader { + BufferedReader reader; + StringTokenizer tokenizer; + public InputReader(InputStream stream) { + reader = new BufferedReader(new InputStreamReader(stream), 32768); + tokenizer = null; + } + String next() { // reads in the next string + while (tokenizer == null || !tokenizer.hasMoreTokens()) { + try { + tokenizer = new StringTokenizer(reader.readLine()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return tokenizer.nextToken(); + } + public int nextInt() { return Integer.parseInt(next()); } // reads in the next int + public long nextLong() { return Long.parseLong(next()); } // reads in the next long + public double nextDouble() { return Double.parseDouble(next()); } // reads in the next double + } + static InputReader r = new InputReader(System.in); + static PrintWriter pw = new PrintWriter(System.out); + public static void main(String[] args) { + + // YOUR CODE HERE + + pw.close(); // flushes the output once printing is done + } +} +``` + +Here's a brief description of the methods in our `InputReader` class, with an instance `r`, and `PrintWriter` with an instance `pw`. + + +| Method | Description | +| ------------------ | ------------------------------------------------------------------- | +| `r.next()` | Reads the next token (up to a whitespace) and returns a `String` | +| `r.nextInt()` | Reads the next token (up to a whitespace) and returns as an `int` | +| `r.nextLong()` | Reads the next token (up to a whitespace) and returns as a `long` | +| `r.nextDouble()` | Reads the next token (up to a whitespace) and returns as a `double` | +| `pw.println()` | Prints the argument to designated output stream and adds newline | +| `pw.print()` | Prints the argument to designated output stream | + + +Here's an example to show how input/output works. Let's say we want to write a program that takes three numbers as input and prints their sum. + +```java +// InputReader template code above +static InputReader r = new InputReader(System.in); +static PrintWriter pw = new PrintWriter(System.out); + +public static void main(String[] args) { + int a = r.nextInt(); + int b = r.nextInt(); + int c = r.nextInt() + pw.println(a + b + c); + pw.close(); +} +``` + +</JavaSection> + +</LanguageSection> + +## File I/O + +<Problems problems={problems.fence} /> + +In USACO, input is read from a file called `problemname.in`. After the program is run, output must be printed to a file called `problemname.out`. Note that you'll have to rename the `.in` and `.out` files depending on the problem. For example, in the above problem you would use `paint.in` and `paint.out`. + +<LanguageSection> + +<CPPSection> + +### Method 1: `freopen` + +You will need the [<cstdio\>](http://www.cplusplus.com/reference/cstdio/) library. + +```cpp +#include <cstdio> +using namespace std; + +int main() { + freopen("problemname.in", "r", stdin); + freopen("problemname.out", "w", stdout); + // rest of your code ... + // can use cin or scanf +} +``` + +### Method 2: [`<fstream>`](http://www.cplusplus.com/reference/fstream/) + +You cannot use C-style I/O (`scanf`, `printf`) with this method. + +```cpp +#include <fstream> +using namespace std; + +int main() { + ifstream fin("problemname.in"); + ofstream fout("problemname.out"); + // rest of your code ... + +} +``` + +</CPPSection> + +<JavaSection> + +We can slightly modify the template above to support file I/O. Note how `r` and `pw` must be initialized in a different way. + +```java +import java.util.*; +import java.io.*; + +public class template { + static class InputReader { + BufferedReader reader; + StringTokenizer tokenizer; + public InputReader() throws FileNotFoundException { + reader = new BufferedReader(new FileReader("problemname.in")); + tokenizer = null; + } + String next() { + while (tokenizer == null || !tokenizer.hasMoreTokens()) { + try { + tokenizer = new StringTokenizer(reader.readLine()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return tokenizer.nextToken(); + } + public int nextInt() { return Integer.parseInt(next()); } + public long nextLong() { return Long.parseLong(next()); } + public double nextDouble() { return Double.parseDouble(next()); } + } + public static void main(String[] args) throws FileNotFoundException, IOException { + InputReader r = new InputReader(); + PrintWriter pw = new PrintWriter(new FileWriter("problemname.out")); + + // YOUR CODE HERE + + pw.close(); // flushes the output once printing is done + } +} +``` + + +Some sources say to use + +```java +PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("problemname.out"))); +``` + +instead. There is no need for this since `PrintWriter` [uses buffered output](https://stackoverflow.com/questions/32177690/is-printwriter-buffered). + +</JavaSection> + +</LanguageSection> + +## Example Solutions - Fence Painting + +<Resources> + <Resource source="USACO" title="Technical Specifications for Contests" url="http://www.usaco.org/index.php?page=instructions" starred>Make sure to read this.</Resource> +</Resources> + +<br /> + +Importantly, USACO will automatically add a newline to the end of your file if it does not end with one. Make sure not to output trailing spaces, or you will get an error such as the following: + +![bad](./Error.png) + +<LanguageSection> + +<CPPSection> + +### Method 1 + +Use [freopen](http://www.cplusplus.com/reference/cstdio/freopen/). If you comment out both of the lines containing `freopen` then the program reads from standard in and writes to standard out as usual. + +```cpp +#include <iostream> +#include <vector> +using namespace std; + +int main() { + freopen("paint.in","r",stdin); // reuse standard in to read from "paint.in" + freopen("paint.out","w",stdout); // reuse standard out to write to "paint.out" + vector<bool> cover(100); + int a, b, c, d; cin >> a >> b >> c >> d; + for (int i = a; i < b; ++i) cover[i] = 1; + for (int i = c; i < d; ++i) cover[i] = 1; + int ans = 0; + for (int i = 0; i < 100; ++i) ans += cover[i]; + cout << ans; + // cout << ans << endl; is OK + // cout << ans << "\n"; is OK + // cout << ans << " "; is NOT OK + // cout << ans << "\n\n"; is NOT OK +} +``` + +### Method 2 + +Use [ifstream & ofstream](http://www.cplusplus.com/doc/tutorial/files/). + +```cpp +#include <fstream> +#include <vector> +using namespace std; + +int main() { + ifstream fin("paint.in"); + ofstream fout("paint.out"); + vector<bool> cover(100); + int a, b, c, d; fin >> a >> b >> c >> d; + for (int i = a; i < b; ++i) cover[i] = 1; + for (int i = c; i < d; ++i) cover[i] = 1; + int ans = 0; + for (int i = 0; i < 100; ++i) ans += cover[i]; + fout << ans; +} +``` + +</CPPSection> + +<JavaSection> + +### Method 1 + +```java +import java.io.*; +import java.util.*; + +public class paintSol { // must be declared in paintSol.java + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new FileReader("paint.in")); + PrintWriter pw = new PrintWriter(new FileWriter("paint.out")); + int[] cover = new int[100]; + + StringTokenizer st = new StringTokenizer(br.readLine()); + int a = Integer.parseInt(st.nextToken()), b = Integer.parseInt(st.nextToken()); + st = new StringTokenizer(br.readLine()); + int c = Integer.parseInt(st.nextToken()), d = Integer.parseInt(st.nextToken()); + + for (int i = a; i < b; i++) cover[i] = 1; + for (int i = c; i < d; i++) cover[i] = 1; + int ans = 0; + for (int i = 0; i < 100; i++) ans += cover[i]; + pw.println(ans); + pw.close(); // make sure to include this line -- flushes the output. + } +} +``` + +### Method 2 + +Alternatively, we can use the `InputReader` given above. + +```java +import java.util.*; +import java.io.*; + +public class template { + static class InputReader { + BufferedReader reader; + StringTokenizer tokenizer; + public InputReader() throws FileNotFoundException { + reader = new BufferedReader(new FileReader("paint.in")); + tokenizer = null; + } + String next() { + while (tokenizer == null || !tokenizer.hasMoreTokens()) { + try { + tokenizer = new StringTokenizer(reader.readLine()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return tokenizer.nextToken(); + } + public int nextInt() { return Integer.parseInt(next()); } + public long nextLong() { return Long.parseLong(next()); } + public double nextDouble() { return Double.parseDouble(next()); } + } + public static void main(String[] args) throws FileNotFoundException, IOException { + InputReader r = new InputReader(); + PrintWriter pw = new PrintWriter(new FileWriter("paint.out")); + + int a = r.nextInt(), b = r.nextInt(); + int c = r.nextInt(), d = r.nextInt(); + + int[] cover = new int[100]; + for (int i = a; i < b; i++) cover[i] = 1; + for (int i = c; i < d; i++) cover[i] = 1; + int ans = 0; + for (int i = 0; i < 100; i++) ans += cover[i]; + + pw.println(ans); + pw.close(); // flush output + } +} +``` + +</JavaSection> + +<PySection> + +See [here](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files) for documentation about file I/O. + +```py +fin = open("paint.in","r") +fout = open("paint.out","w") +cover = [0 for i in range(100)] +a,b = map(int,fin.readline().split()) +c,d = map(int,fin.readline().split()) +for i in range(a,b): + cover[i] = 1 +for i in range(c,d): + cover[i] = 1 +ans = 0 +for i in range(100): + ans += cover[i] +fout.write(str(ans)) +``` + +</PySection> + +</LanguageSection>
\ No newline at end of file diff --git a/content/1_Intro/Intro.md b/content/1_Intro/Intro.md deleted file mode 100644 index 44cb6e6..0000000 --- a/content/1_Intro/Intro.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -id: intro -title: Introduction -author: Nathan Wang, Benjamin Qi, Darren Yao -description: Welcome to the guide! We'll introduce what programming competitions are and how this guide is organized. ---- - -A [programming competition](https://en.wikipedia.org/wiki/Competitive_programming) generally lasts for several hours and consists of a set of problems. These problems are not open problems; they have already been solved by the problem writers and testers and are designed to be solved in the short timeframe of a contest. In general, each problem in competitive programming is solved with a two-step process: - - 1. coming up with the algorithm, which involves problem solving skills and intuition - 2. implementing the algorithm, which requires programming skills to translate the algorithm into working code. - -For each problem, you submit the completed code to a grader, which checks the answers calculated by your program against a set of predetermined test cases. For each problem, you are given a time limit (usually 2 seconds) and a memory limit (usually 256 [megabytes](https://en.wikipedia.org/wiki/Megabyte)) that your program must satisfy. - -For those of you with experience in software development, note that competitive programming is quite different, as the goal is to write programs that compute the correct answer, run quickly, and can be implemented quickly. Note that nowhere was maintainability of code mentioned. You don't need to bother documenting your code because it only needs to be readable to you during the contest. - -<optional-content title="More Information"> - - - Videos - - [William Lin - What is Competitive Programming?](https://www.youtube.com/watch?time_continue=1&v=ueNT-w7Oluw) - - [Kamil Debowski - Interview with a Competitive Programmer](https://www.youtube.com/watch?v=F4rykKLcduI) - - [Task](https://open.kattis.com/contests/mcpc19open/problems/basketballoneonone) that was mentioned in video. - - Reading - - [PAPC Ch 1](http://www.csc.kth.se/~jsannemo/slask/main.pdf) - -</optional-content> - -## USACO Contest Format - -The [USA Computing Olympiad](http://www.usaco.org/index.php?page=contests) is a national programming competition that occurs four times a year, with December, January, February, and US Open (March) contests. The regular contests are four hours long, and the US Open is five hours long. Each contest contains three problems. Solutions are evaluated and scored against a set of predetermined test cases that are not visible to the student. Scoring is out of 1000 points, with each problem being weighted equally (\~333 points). There are four divisions of contests: Bronze, Silver, Gold, and Platinum. After each contest, students who meet the contest-dependent cutoff for promotion will compete in the next division for future contests. - -## About This Guide - -### Languages - -- For Bronze and Silver, we will provide code snippets in C++, Java, and Python. -- For Gold, we will provide code snippets in C++ and Java and (sometimes) Python. -- For Platinum, code snippets may only be provided in one language (typically either C++ or Java). - -### Guidelines - - - For Bronze, Silver, and Gold contestants, we aim to be a "**one stop shop**," meaning that this is the only site you have to use to be exposed to most (if not all) of the topics required for Bronze - Gold. - - Hopefully, this will reduce the amount of time you have to spend finding quality training resources. - - "**Don’t Reinvent the Wheel**:" we'll link to online resources that already exist whenever possible instead of rewriting tutorials ourselves. - - **Not *just* a collection of links** - - There are plenty of resources out there, but we do not expect you to click through all of them to find the information you want. - - This means in addition to the link itself, we will try our best to provide information about what the link is about as well as the quality of the link. - - We won't write something like "learn DP, here are 50 links that can teach you that." Instead, we will write "learn DP by first reading this one article, then reading this other article. For reference, here are some other links you can explore as you wish." - - For Platinum contestants, there are too many topics for us to effectively cover all of them. - - We'll try our best to cover the main topics, but if you want to do well in Platinum, you will have to find additional resources on your own in addition to this site. - - Platinum modules may have more vague explanations compared to earlier divisions. If you're confused, you'll have to research the topic more on your own. - -### Modules - -All material in this guide will be grouped into **modules** such as the one you're reading right now. - -<info-block title="Pro Tip"> - -Hopefully these are helpful. - -</info-block> - -#### Lesson - - - Consists of text, videos, and simple problems. - - Goal is to introduce you to the concept. - - Everything should be completed in order. - - Any problems here will generally be pure implementation. - -<optional-content title="Optional Content"> - -It's okay to skip over these. Some material might not be useful for competitive programming. - -</optional-content> - -#### Practice - - - Link the relevant past USACO problems (and other recommended problems). - - Problems should be sorted in order of how they are recommended be completed. - - Add comments regarding solution sketches. - - Possibly include additional problems. - - Difficulty is relative to division. (?)
\ No newline at end of file diff --git a/content/1_Intro/Intro.mdx b/content/1_Intro/Intro.mdx new file mode 100644 index 0000000..4c7e9b5 --- /dev/null +++ b/content/1_Intro/Intro.mdx @@ -0,0 +1,41 @@ +--- +id: intro +title: Introduction to Competitive Programming +author: Nathan Wang, Benjamin Qi, Darren Yao +description: What is competitive programming? Let's take a look! +--- + +## Programming Competitions + +A [programming competition](https://en.wikipedia.org/wiki/Competitive_programming) generally lasts for several hours and consists of a set of problems. These problems are not open problems; they have already been solved by the problem writers and testers and are designed to be solved in the short timeframe of a contest. In general, each problem in competitive programming is solved with a two-step process: + + 1. coming up with the algorithm, which involves problem solving skills and intuition + 2. implementing the algorithm, which requires programming skills to translate the algorithm into working code. + +For each problem, you submit the completed code to a grader, which checks the answers calculated by your program against a set of predetermined test cases. For each problem, you are given a time limit (usually 2 seconds) and a memory limit (usually 256 [megabytes](https://en.wikipedia.org/wiki/Megabyte)) that your program must satisfy. + +For those of you with experience in software development, note that competitive programming is quite different, as the goal is to write programs that compute the correct answer, run quickly, and can be implemented quickly. Note that nowhere was maintainability of code mentioned. You don't need to bother documenting your code because it only needs to be readable to you during the contest. That being said, you probably want to maintain a bare minimum level of readability so you can keep track of what's going on. + +<!-- <Optional title="More Information"> --> + +<div class="yt-video-container my-8"><iframe width="560" height="315" src="https://www.youtube.com/embed/ueNT-w7Oluw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div> + +<Resources> + <Resource source="William Lin" title="Video - What is Competitive Programming?" url="https://www.youtube.com/watch?time_continue=1&v=ueNT-w7Oluw">video shown above</Resource> + <Resource source="Kamil Debowski" title="Video - Interview with a Competitive Programmer" url="https://www.youtube.com/watch?v=F4rykKLcduI"></Resource> + <Resource source="CPH" title="1 - Introduction">algorithms & programming contests</Resource> + <Resource source="IUSACO" title="1 - The Beginning"></Resource> + <Resource source="PAPS" title="1 - Algorithms & Problems">examples of algorithms</Resource> +</Resources> + +[Task](https://open.kattis.com/contests/mcpc19open/problems/basketballoneonone) that was mentioned in Kamil's video. + +<!-- </Optional> --> + +## USACO + +<Resources> + <Resource source="USACO" title="Contests" url="http://www.usaco.org/index.php?page=contests" starred>more abount contest format</Resource> +</Resources> + +The **USA Computing Olympiad** is a national programming competition that occurs four times a year, with December, January, February, and US Open (March) contests. The regular contests are four hours long, and the US Open is five hours long. Each contest contains three problems. Solutions are evaluated and scored against a set of predetermined test cases that are not visible to the student. Scoring is out of 1000 points, with each problem being weighted equally (\~333 points). There are four divisions of contests: Bronze, Silver, Gold, and Platinum. After each contest, students who meet the contest-dependent cutoff for promotion will compete in the next division for future contests. diff --git a/content/1_Intro/Lambda.mdx b/content/1_Intro/Lambda.mdx new file mode 100644 index 0000000..48c561d --- /dev/null +++ b/content/1_Intro/Lambda.mdx @@ -0,0 +1,101 @@ +--- +id: lambda +title: Lambda Expressions +author: Benjamin Qi +description: "Unnamed function objects capable of capturing variables in scope." +--- + +<LanguageSection> + +<CPPSection> + + +## Introduction + +<Resources> + <Resource source="CPP" url="https://en.cppreference.com/w/cpp/language/lambda" title="Lambda expressions">reference</Resource> + <Resource source="UMich" url="http://umich.edu/~eecs381/handouts/Lambda.pdf" title="Using C++ Lambdas" starred> </Resource> + <Resource source="SO" url="https://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11" title="What is a lambda expression in C++11?"> </Resource> + <Resource source="Microsoft" url="https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=vs-2019" title="Lambda Expressions in C++"> </Resource> +</Resources> + +Anything more beginner-friendly? + +<IncompleteSection /> + +<Resources title="FAQ"> + <Resource source="SO" url="https://stackoverflow.com/questions/41121441/type-of-a-lambda-function-using-auto" title="Type of a lambda function using auto?"> + </Resource> + <Resource source="SO" url="https://stackoverflow.com/questions/11323811/what-is-meant-by-retain-state-in-c" title="What is meant by 'retain state' in c++?"> </Resource> +</Resources> + +## Recursive Lambdas + +<Resources> + <Resource source="open-std" url="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0200r0.html" title="Y Combinator Proposal" starred> </Resource> + <Resource source="RIP Tutorial" url="https://riptutorial.com/cplusplus/example/8508/recursive-lambdas" title="Recursive Lambdas"> </Resource> +</Resources> + +If we add the following from the link above in C++14: + +```cpp +namespace std { + +template<class Fun> +class y_combinator_result { + Fun fun_; +public: + template<class T> + explicit y_combinator_result(T &&fun): fun_(std::forward<T>(fun)) {} + + template<class ...Args> + decltype(auto) operator()(Args &&...args) { + return fun_(std::ref(*this), std::forward<Args>(args)...); + } +}; + +template<class Fun> +decltype(auto) y_combinator(Fun &&fun) { + return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun)); +} + +} // namespace std +``` + +Then we can have code like the following! + +```cpp +int main() { + cout << y_combinator([](auto gcd, int a, int b) -> int { + return b == 0 ? a : gcd(b, a % b); + })(20,30) << "\n"; // outputs 10 +} +``` + +Looks like [ecnerwal](https://codeforces.com/contest/1375/submission/86008510) uses these a lot ... + + +</CPPSection> + +</LanguageSection> + +<!-- +import InputOutput from "../../additionalcontent/Input_Output.mdx"; +import DebuggingCpp from "../../additionalContent/Debugging_Cpp.mdx"; +import ShortenCpp from "../../additionalContent/Shorten_Cpp.mdx"; + +<InputOutput /> + +<LanguageSection> + +<CPPSection> + +<DebuggingCpp /> +<ShortenCpp /> + +</CPPSection> + +<JavaSection /> +<PythonSection /> + +</LanguageSection> -->
\ No newline at end of file diff --git a/content/1_Intro/Languages.md b/content/1_Intro/Languages.md deleted file mode 100644 index cbde1b4..0000000 --- a/content/1_Intro/Languages.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -id: lang -title: Languages -author: Nathan Wang, Benjamin Qi -description: What languages you can use for programming contests and what you're expected to know before continuing. ---- - -## Choosing a Language - -The most popular languages that USACO supports are [C++11](https://en.wikipedia.org/wiki/C%2B%2B), [Java](https://en.wikipedia.org/wiki/Java_(programming_language)), and [Python 3](https://en.wikipedia.org/wiki/Python_(programming_language)). In general, we recommend the following: - - - If you already know one of these languages, just use it. - - If you know multiple languages, we recommend you pick C++ over Java, and Java over Python. - - For Bronze, any language will do. - - For Silver, Gold, and Platinum, Python is not recommended. - -Keep in mind that it's easy to switch languages down the road. Don't get caught up on which language to choose. Just pick the one you feel most comfortable with! - -### Language References - -All of these are provided at the IOI aside from the additional C++ reference. - - - [C++](https://en.cppreference.com/w/) - - [Additional C++ Reference](http://www.cplusplus.com/) - - [Java](https://docs.oracle.com/javase/8/docs/api/overview-summary.html) - - [Python3](https://docs.python.org/3/reference/) - -## Expected Knowledge - -The remainder of this guide assumes that you know the basics of how to code in one of the languages listed above, including the following topics: - -- Variables - - Data types -- Reading Input -- Writing Output -- Loops -- If / Else -- Logical operators -- Functions - - Basic Recursion (a function calling itself) -- Arrays - - Multidimensional Arrays - -In particular, contestants using Java should be familiar with roughly the first half of AP Computer Science A. - - - If you do not meet these prerequisites, see the links below to get started. - - Familiarity with [competition math](https://github.com/bqi343/USACO/blob/master/Resources/Competition%20Math.md) (ex. AIME qualification) is helpful but not required. - -## Resources for Learning How to Code - -[Sololearn](https://www.sololearn.com/) has courses on C++, Java, and Python. You don't have to complete the full course. - -### C++ - - - If you use Sololearn, we recommend you finish everything up to (but not including) "More on Classes." - - [Ch 2 of PAPS](http://www.csc.kth.se/~jsannemo/slask/main.pdf) also contains a C++ tutorial. Use one of these two resources (or find your own) to learn C++. - -<info-block title="Pro Tip"> - -You do not need to learn pointers (for now). Knowledge of structs and classes is useful but not required. - -</info-block> - - -## Resources for Getting Started - -<optional-content> - -Note: We don't agree with all views expressed in the links below. Let us know what works (or doesn't) for you. - - - [CodeSignal](https://codesignal.com/) - - can practice basics with "Arcade," "Interview Practice" - - [Philippines OI: Prepare](https://noi.ph/prepare/) - - lots of links! - - [IOI - Getting Started](https://ioinformatics.org/page/getting-started/14) - - not so up to date - - [Quora - Schedule for Beginners (Joshua Pan)](https://www.quora.com/What-is-a-good-schedule-to-follow-for-becoming-better-at-competitive-programming-for-beginners) - -</optional-content>
\ No newline at end of file diff --git a/content/1_Intro/Modules.mdx b/content/1_Intro/Modules.mdx new file mode 100644 index 0000000..e4f7067 --- /dev/null +++ b/content/1_Intro/Modules.mdx @@ -0,0 +1,113 @@ +--- +id: modules +title: Introducing Modules +author: Nathan Wang, Benjamin Qi +description: How each module is structured. +prerequisites: + - using-this-guide +--- + +The material in this guide will be grouped into **modules** such as the one you're reading right now. A module generally consists of three parts: + +1. **Lesson**: Introduces the concept. +2. **Implementations**: Code! +3. **Practice Problems**: Teaches you how to apply the concept to various problems. + +<!-- 3. **Additional Content**: Optional resources related to the module, for those who are interested --> + +## Lesson + +The lesson is a curated collection of external resources, problems, and supplementary text we've written ourselves. + + - We'll link to online resources that already exist whenever possible. + - The goal is to introduce you to the concept. + - Everything is meant to be completed in order. + - It usually begins with at least one **standard problem**, marked "Intro" in the difficulty column. + - The standard problem is a direct application of the concept, and is useful in testing your implementation. + - We'll **star** external resources that we highly recommend you read. Feel free to read the others if you don't understand something. If multiple modules cover essentially the same material, at most one should be starred. + +## Implementations + +Usually the lesson is followed by an implementation of the solution to the standard problem. + + - C++ code will always be provided. + - Java code will be provided (at least) for Bronze - Gold. + - Python code will be provided (at least) for Bronze and some of Silver. + +Example input / output will be provided if not already contained within the statement of the standard problem. Implementations we provide will follow certain [conventions](./code-con). + +## Practice Problems + +After reading the module lesson, you'll be given a lot of problems (from various sources, not just USACO) to practice applying the concept you've learned. + +- The problems are roughly sorted in order of how they are recommended to be completed. +- You don't have to solve every problem, just enough to feel comfortable with the module. We've starred the ones we found most interesting. + +### Problem Editorials + +We'll try our best to write full editorials for problems which don't have them (or if existing editorials are poorly written). _Starred problems will generally have better editorials._ + +_Pre-release note_: Currently, very few problems have full editorials. We've provided some temporary _solution sketches_ (very short solution explanations) while we continue working on this guide. If you believe an editorial is needed for a problem, please let us know using the "Contact Us" button. + +### Problem Difficulty (Bronze - Gold) + +Difficulty represents how challenging a problem is expected to be to someone after they read through the module, **not** how difficult the problem is in general. Therefore, it is **not** comparable across modules (even of the same division). + +Difficulty ranges from **Very Easy** to **Insane**. + +- **Very Easy** problems are related to the module, but you should be able to do them relatively quickly before reading the resources. +- **Easy** problems can be solved relatively quickly by someone who is familiar with the module, and they should be approachable by someone who has just finished reading the starred resources. +- **Normal** problems require a bit more thinking. +- **Hard** problems may require a lot (?) of time to approach. +- **Very Hard** problems challenge even the strongest contestants in the corresponding division. Often require multiple levels of observation and more knowledge than provided by the module. +- **Insane** problems shouldn't appear on a (reasonable) contest of the corresponding division. Don't worry about solving these until you've reached a higher division. + +<!-- ## Additional Content + +Some modules may also have additional content at the end that isn't needed for USACO, but may be interesting. Feel free to read or skip them as you'd like. --> + +## Interactive Elements + +Sometimes modules will contain interactive elements. Here are some of the more common ones. + +<Info title="Pro Tip"> + +Helpful bits of advice provided by the author. + +<!-- Tips on how to become legend in CP and better at competitve programming by Benjamin Qi, exactly what you wanted when you spam DM'd him! Definitely take these tips seriously. Don't uncomment this. --> + +</Info> + +<Spoiler title="Hidden Content"> + +In some modules, codes or solutions will be placed in **spoiler blocks** like this. In these cases, you should think about the problem or try to implement it yourself before revealing the solution or code. + +</Spoiler> + +<Optional title="Optional Content"> + +Not all content in this guide is essential to competitive programming. Skipping over optional content is fine, but if you're interested, feel free to explore further as well. + +</Optional> + +<Warning> + +A warning block like this will contain common errors that you should avoid. + +</Warning> + +<IncompleteSection> + +These will appear frequently. + +</IncompleteSection> + +<!-- Difficulty should be comparable across a division. Say that you have *almost-solved* a question if you scored at least $n-2$ out of $n$ test cases. At least for platinum, difficulty levels should correspond approximately to the following USA Pre-college almost-solve rates on a USACO contest: + + - Easy: $\ge 40\%$ (ex. Fort Moo, Team Building, Redistricting) + - Normal: $\ge 20\%$ (ex. Card Game, Balancing, Gathering) + - Hard: $\ge 10\%$ (ex. Mooriokart, Train Tracking 2, Friendcross) + +Old gold problems should probably be bumped up one level. --> + +<!-- Re: above. Almost-solved is subjective to problem though. Incorrect greedy "almost solves" deleg."" --> diff --git a/content/1_Intro/Olympiads.mdx b/content/1_Intro/Olympiads.mdx new file mode 100644 index 0000000..dbcd9ff --- /dev/null +++ b/content/1_Intro/Olympiads.mdx @@ -0,0 +1,71 @@ +--- +id: oly +title: "Olympiads" +author: Benjamin Qi +description: Once you've reached Platinum, it may be helpful to practice with problems from other (inter)national olympiads. +--- + +> Hello, Which online judge should I practice more to do well in **IOI** ? + +> the closest OJ for IOI style? + +## [USACO](http://www.usaco.org/) + + * Older Problems + * [TJ website](http://tjsct.wikidot.com/usaco/) + * [Korean website to submit ...](https://www.acmicpc.net/category/106) + * [USACO Training](http://train.usaco.org/usacogate) + * not particularly beginner-friendly but still helpful + * somewhat outdated in the sense that it lacks topics which are now quite common (ex. segment tree) + * if you're unsure about whether it will be useful, you might as well try it and see where you get stuck :P + * personally, I did the first five chapters in one summer (though I had to look up some hints ...) + * [article :o](https://www.usenix.org/legacy/bodinfo/bod/bodmarch10/future.pdf) + +## [oj.uz](https://oj.uz/) + +The [OI Checklist](https://oichecklist.pythonanywhere.com/) is a great way to track your progress. :) + +<Resources> + <Resource source="IZhO" title="International Zhautykov Olympiad (Kazakhstan)" url="https://izho.kz/"></Resource> + <Resource source="APIO" title="Asia-Pacific Informatics Olympiad" url="http://apio-olympiad.org/"></Resource> + <Resource source="COCI" title="Croatian Open Contests in Informatics" url="http://hsin.hr/coci/">not full feedback ...</Resource> + <Resource source="JOI" title="Japanese Olympiad in Informatics" url="https://www.ioi-jp.org/"></Resource> + <Resource source="LMIO" title="Lithuanian Olympiad in Informatics" url="http://online.lmio.lt/"></Resource> + <Resource source="InfO(1) Cup" title="InfO(1) Cup" url="http://info1cup.com/"></Resource> +</Resources> + +## [IOI](https://ioinformatics.org/) + +Can submit on more recent problems on oj.uz. Also see the following: + + - [Yandex](https://contest.yandex.com/ioi/) + - [WCIPEG](https://wcipeg.com/problems/cat%3Dioi%2Cshow%3D50) + - [DMOJ](https://dmoj.ca/problems/?category=5) + +Misc: + + - [IOI Syllabus](https://people.ksp.sk/~misof/ioi-syllabus/) + - [kostka - List of 2019 IOI Participants](http://weaselcrow.com/pro/cf/ioi/19/) + +## [CSES](https://cses.fi/) + +<Resources> + <Resource source="BOI" title="Baltic Olympiad in Informatics" url="http://www.boi2017.org/">2017 website</Resource> + <Resource source="CEOI" title="Central European Olympiad in Informatics" url="http://ceoi.inf.elte.hu/"> </Resource> +</Resources> + +## Other + +See the [IOI members page](https://ioinformatics.org/page/members/7) for additional links. + + * [Poland](https://szkopul.edu.pl/portal/) + * [Solutions (in Polish)](https://www.oi.edu.pl/l/40/) + * [Canada](https://cemc.math.uwaterloo.ca/contests/computing.html) + * [WCIPEG](https://wcipeg.com/problems/cat%3Dccc%2Cshow%3D50) + * [DMOJ](https://dmoj.ca/problems/?category=24) + * [Philippines](https://noi.ph/past-problems/) + * [Indonesia](https://competition.ia-toki.org/contests) + * [Australia](https://orac.amt.edu.au/) + * [Italy](https://training.olinfo.it/#/overview) + * [International Informatics Olympiad in Teams](http://ioit.altervista.org/2018-teams-and-contests-.html) (IOIT) + * [Balkan Olympiad in Informatics](https://boi2019.epy.gr/) (2019 website)
\ No newline at end of file diff --git a/content/2_General/Practicing.md b/content/1_Intro/Practicing.mdx index 8f7fe22..38c0190 100644 --- a/content/2_General/Practicing.md +++ b/content/1_Intro/Practicing.mdx @@ -1,13 +1,13 @@ --- id: practicing -title: How to Practice +title: How to Practice author: Many description: How to practice and when to read editorials (analyses) according to various USACO competitors. --- Knowing when to "give up" on a problem and start reading its editorial is challenging. Below are the opinions of various individuals. Note that "give up" is in quotes, because one still learns when they "give up" and read an editorial! -## Darren Yao (Intro to USACO 1.3) +## Darren Yao Reaching a high level in competitive programming requires dedication and motivation. For many people, their practice is inefficient because they do problems that are too easy, too hard, or simply of the wrong type. @@ -17,11 +17,11 @@ In general, I think it’s fine to read the solution relatively early on, as lon - On a bronze problem, read the solution after 15-20 minutes of no meaningful progress, after you’ve exhausted every idea you can think of. - On a silver problem, read the solution after 30-40 minutes of no meaningful progress. - - IMPORTANT: When you get stuck and consult the solution, you should not read the entire solution at once, and you certainly shouldn’t look at the solution code. Instead, it’s better to read the solution step by step until you get unstuck, at which point you should go back and finish the problem, and implement it yourself. Reading the full solution or its code should be seen as a last resort. + - I think that when you get stuck and consult the solution, you should not read the entire solution at once, and you certainly shouldn’t look at the solution code right away. Instead, it’s better to read the solution step by step until you get unstuck, at which point you should go back and finish the problem, and implement it yourself. Reading the full solution or its code should be seen as a last resort. Problems that you practice with should be of the appropriate difficulty. You don't necessarily need to complete all the exercises at the end of each module, just do what you think is right for you. A problem at the right level of difficulty should be one of two types: either you struggle with the problem for a while before coming up with a working solution, or you miss it slightly and need to consult the solution for some small part. If you instantly come up with the solution, a problem is likely too easy, and if you're missing multiple steps, it might be too hard. -[This](https://web.evanchen.cc/FAQs/raqs.html) and [this](https://usamo.wordpress.com/2019/01/31/math-contest-platitudes-v3/) are two blog posts by Evan Chen that I find quite insightful. They discuss such things as time management, the problem-solving process, and other tips that you may find useful. +[This](https://web.evanchen.cc/FAQs/raqs.html) and [this](https://usamo.wordpress.com/2019/01/31/math-contest-platitudes-v3/) are two blog posts by Evan Chen that I find quite insightful. They discuss such things as time management, the problem-solving process, and other tips that you may find useful. See [my FAQ](https://darren-yao.gitbook.io/darren-s-blog/usaco-faq) for more information. ## Nathan Wang @@ -29,10 +29,7 @@ My personal opinion is that it is okay to give up early when solving CP problems Sometimes I spend as little as 15-20 minutes on a problem before reading the editorial or at least glancing at solution code. Other times I may spend significantly longer. -CP editorials generally aren't the best (with the exception of USACO editorials, -which are pretty good) so I often spend a lot of time trying to understand the -solution even after "giving up" and reading the editorial. I think it's good -enough to implement the code without having the editorial open. +CP editorials generally aren't the best (with the exception of USACO editorials, which are pretty good) so I often spend a lot of time trying to understand the solution even after "giving up" and reading the editorial. I think it's good enough to implement the code without having the editorial open. My justification for why I think it's okay to give up so early is as follows: @@ -47,8 +44,7 @@ My justification for why I think it's okay to give up so early is as follows: you read the editorial so now you do, then you've learned a lot more. - In other words, reading editorials is a _good_ thing, not a bad thing! -Overall, I would just say to "give up" when you feel like giving up, whether that's -in five hours or in 15 minutes :) +Overall, I would just say to "give up" when you feel like giving up, whether that's in five hours or in 15 minutes :) ## Siyong Huang @@ -78,6 +74,8 @@ I'm impatient, so usually I go with the first option. Sometimes I end up reading In any case, if you thought about a problem a lot during a contest but didn't end up solving it, then I don't see any reason not to read the editorial when it comes out (vs. continuing to think about it on your own). Also, you should always implement the solution afterwards! +You can also check my [Github](https://github.com/bqi343/USACO/blob/master/Resources/Solving%20Problems.md) and my [IOI interview](https://ioinformatics.org/interview/interview-benjamin-qi/27?event=july-2020). + ## Nathan Chen Read the editorial when you feel like you've stopped making progress; that could be from 1 to 5 hours. However, the most important part about reading the editorial is that you understand the topic and try to think about what similar problems look like. Being generally curious is a good way to practice algorithmic thinking. @@ -86,14 +84,20 @@ Read the editorial when you feel like you've stopped making progress; that could I follow three guidelines (from most important to least important) -1. Having fun, just doing whatever you feel like doing -2. Spend about the same amount of time that you would be able to during a real contest -3. Whether you are making progress or not +1. Having fun, just doing whatever you feel like doing. +2. Spend about the same amount of time that you would be able to during a real contest. +3. Whether you are making progress or not. ## Eric Wei I think the most important thing regarding practicing is to try to get something out of every problem, whether it's a new algorithm or idea, an implementation trick that can help in the future, or just a bug you hopefully won't mess up in the future. That being said, editorials are more useful once you've been stuck for a while; I think the exploration that happens from being a little stuck is often instructive (and good practice for contests, when it's your only option). But at some point the problem's more frustrating than helpful, and sometime before this is probably the right time to take a hint or read the editorial. +## Michael Cao + +Do hard problems, and try to learn something from them. If you read the editorial, think about how you would arrive at the solution if you were to solve it again. Also, it's important to implement every problem you read the editorial of. Most importantly, don't burn yourself out, or you'll just be worse off in the end because your practice isn't efficent. + ## Additional - - [E869120 - A Way to Practice CP](http://codeforces.com/blog/entry/53341)
\ No newline at end of file +<Resources> + <Resource source="CF" title="E869120 - A Way to Practice CP" url="blog/entry/53341">going from CF rating 1000 to 2000</Resource> +</Resources> diff --git a/content/2_General/Proposing.md b/content/1_Intro/Proposing.mdx index aa88fb7..4fbfd02 100644 --- a/content/2_General/Proposing.md +++ b/content/1_Intro/Proposing.mdx @@ -5,12 +5,12 @@ author: Benjamin Qi description: Includes how you should (preferably) format your submissions. --- -Anyone can propose problems for monthly contests. Email your proposal to Professor Dean. In the [past](http://www.usaco.org/index.php?page=viewproblem2&cpid=817), contestants have even written problems for their own divisions! +Anyone can propose problems for monthly contests. Email your proposal to Professor Dean. In the [past](http://www.usaco.org/index.php?page=viewproblem2&cpid=817), contestants have even written problems for their own divisions (though this is not common). - All problems should have 10 test cases at minimum (I believe that the maximum was 21 for [valleys](http://www.usaco.org/index.php?page=viewproblem2&cpid=950)). You do not need to include these in your proposal. - All statements must eventually be converted to the following format. It's not required, but please save us time by following it as best you can. -<spoiler title="2020 Open Gold - Favorite Colors"> +<Spoiler title="2020 Open Gold - Favorite Colors"> ``` http://www.usaco.org/index.php?page=viewproblem2&cpid=1042 @@ -64,4 +64,4 @@ favorite color 1. [/section] ``` -</spoiler>
\ No newline at end of file +</Spoiler>
\ No newline at end of file diff --git a/content/2_General/Resources.md b/content/1_Intro/Resources.mdx index e890be1..06216b5 100644 --- a/content/2_General/Resources.md +++ b/content/1_Intro/Resources.mdx @@ -18,25 +18,23 @@ description: A bunch of helpful links. - *Will be mentioned extensively in later modules.* - [Guide to Competitive Programming](https://www.amazon.com/Guide-Competitive-Programming-Algorithms-Undergraduate/dp/3319725467) is a paid book based off CPH - Can currently download PDF for [free](https://link.springer.com/book/10.1007/978-3-319-72547-5)! - - Intro to USACO - Darren Yao - - Bronze, Silver - - [Java](http://darrenyao.com/usacobook/java.pdf) - - [C++](http://darrenyao.com/usacobook/cpp.pdf) - - *Some modules are from this book.* + - [Principles of Algorithmic Problem Solving](http://www.csc.kth.se/~jsannemo/slask/main.pdf) - Johan Sannemo + - practice problems from Kattis + - Intro to USACO - Darren Yao + - *Some modules are based off chapters from this book.* + - covers most of Bronze, Silver + - [Java](http://darrenyao.com/usacobook/java.pdf), [C++](http://darrenyao.com/usacobook/cpp.pdf) versions - Competitive Programming Book - Steven Halim, Felix Halim - - [Competitive Programming Book 1](http://www.comp.nus.edu.sg/~stevenha/myteaching/competitive_programming/cp1.pdf) is freely available but old + - [Competitive Programming 2](https://www.comp.nus.edu.sg/~stevenha/myteaching/competitive_programming/cp2.pdf) is freely available but old - [Competitive Programming 4](https://cpbook.net/) is the latest edition of the book (with significant additions) but costs money. - - Release date is July 19th, 2020. - [CS Guide](https://github.com/alwayswimmin/cs_guide) - Samuel Hsiang, Alexander Wei, Yang Liu - - Also see the following from TJHSST: - - [TJ Senior Computer Team](https://activities.tjhsst.edu/sct/) - - [TJIOI](https://github.com/tjsct/tjioi-study-guide) - - some basics - - [Principles of Algorithmic Problem Solving](http://www.csc.kth.se/~jsannemo/slask/main.pdf) - Johan Sannemo - - practice problems from Kattis + - The [TJ Senior Computer Team](https://activities.tjhsst.edu/sct/) website might also be of interest. - [Cracking the Coding Interview](http://www.crackingthecodinginterview.com/) - good book specifically for programming interviews + <!-- - some more from TJHSST: --> + <!-- - [TJIOI](https://github.com/tjsct/tjioi-study-guide) (just basics) --> + ## Courses - [Competitive Programming Course](https://github.com/SuprDewd/T-414-AFLV) - Bjarki Ágúst Guðmundsson @@ -46,7 +44,7 @@ description: A bunch of helpful links. - [VPlanet](https://www.vplanetcoding.com/courses) - Riya Arora - courses for various levels - [Cousera Algorithms Pt 1 (and Pt 2)](https://www.coursera.org/learn/algorithms-part1) - Kevin Wayne, Robert Sedgewick - - Java + - Java (has anyone done this course?) ## Tutorials @@ -67,4 +65,11 @@ description: A bunch of helpful links. - ICPC book - past USACO solutions - [ekzlib](http://ekzlib.herokuapp.com) - Eric Zhang - - (advanced) implementations
\ No newline at end of file + - (advanced) implementations + +## USACO Problems + + - [Silver - Platinum Todo Checklist](https://darren-yao.gitbook.io/darren-s-blog/usaco-faq#what-should-i-use-to-keep-track-of-which-usaco-problems-ive-done) + - [Benq - Platinum Categorization](https://github.com/bqi343/USACO/blob/master/Contests/USACO%20Links/Division-Specific/Platinum.md) (and some for other divisions) + - [USACO solutions](https://github.com/bqi343/USACO/tree/master/Contests/USACO%20Solutions) + - [Silver Categorization](https://docs.google.com/document/d/1ba2jr2PFrtxHuGfvdjYVKX-8VzkQAdhpiMLlpXxiAOM/edit)
\ No newline at end of file diff --git a/content/1_Intro/Running_Code.mdx b/content/1_Intro/Running_Code.mdx new file mode 100644 index 0000000..0f9c6be --- /dev/null +++ b/content/1_Intro/Running_Code.mdx @@ -0,0 +1,421 @@ +--- +id: running-code +title: Running Code +author: Benjamin Qi, Hankai Zhang, Anthony Wang, Nathan Wang, Nathan Chen +description: Options for running your language of choice. +prerequisites: + - choosing-lang +--- + +Please let us know if these installation instructions do not work for you. + +# Running Code Online + +- [OnlineGDB](https://www.onlinegdb.com/) + - C++, Python + - online compiler with embedded GDB debugger + - supports files and file I/O + - can be buggy sometimes +- [CSAcademy](https://csacademy.com/workspace/) + - C++, Python, Java + - pretty nice (unless you get "Estimated Queue Time: ...") + - "saved locally" will **not** save your code if you close the tab, press Command-S to save. +- [Ideone](http://ideone.com/) + - C++, Python, Java + - okay ... has the bare minimum you need + - sometimes erases your code when you first create it (so get in the habit of copying your code first) + +<Warning> + +If you use Ideone during an online contest (such as a Codeforces round), you **must** ensure that your code is set to be private. **You might be punished for cheating if another person finds and copies your code during the contest, even if you never intentionally provide the code to anyone.** + +Even if you are not doing an online contest, you might want to set your code to be private for other reasons. + +</Warning> + +You can also share code with [pastebin](https://pastebin.com/) or [hastebin](https://hastebin.com/). + +# Using Command Line + + +<LanguageSection> + +<CPPSection> + +You can run your C++ programs from the command line and use a text editor of your choice. Read below for different options to create and run C++ programs. + +## Text Editors + +<Resources> + <Resource title="Sublime Text 3" url="https://www.sublimetext.com/" starred> + Fast, lightweight. Unlimited free evaluation period, though it will repeatedly ask you to purchase a license. + </Resource> + <Resource title="Atom" url="https://atom.io/"> + From the makers of Github. + </Resource> + <Resource title="Vim" url="https://www.vim.org/"> + Classic text editor, usually preinstalled on Linux. + </Resource> +</Resources> + +Vim is probably the easiest way to print syntax-highlighted code on Mac, see the response to [this post](https://stackoverflow.com/questions/1656914/printing-code-with-syntax-highlighting). + +### Sublime Text Notes (Ben) + +- I prefer the **Mariana** color scheme in place of the default. + - open command palette (Cmd-Shift-P) -> change color scheme +- [`subl` symlink](https://www.sublimetext.com/docs/3/osx_command_line.html) + - Using `/usr/local/bin/subl` instead of `~/bin/subl` worked for me on OS X Mojave. +- [Package - Sublime Linter (GCC)](https://packagecontrol.io/packages/SublimeLinter-gcc) + - highlights compilation errors and warnings from `-Wall` +- [Package - Fast Olympic Coding](https://github.com/Jatana/FastOlympicCoding) (I don't use) + - test manager can be useful + - linting is covered by the above + - stress testing is covered in "Debugging" + - can't get debug to work + +<!-- - [Editing Build Settings](https://stackoverflow.com/questions/23789410/how-to-edit-sublime-text-build-settings) + - no need to do this if you just use command line to compile & run --> + + + +## On Linux + +GCC is usually preinstalled on most Linux distros. You can check if it is installed with + +``` +whereis g++ +``` + +If it is not preinstalled, you can probably install it using your distro's package manager. + +## On Windows + +### [MinGW](http://mingw.org/) + +First, download and run the [MinGW installer](https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/). Once it's installed, open the MinGW Installation Manager, click on Basic Setup on the left, and select `mingw32-gcc-g++-bin` for installation. + +[Adding MinGW to PATH](https://www.rose-hulman.edu/class/csse/resources/MinGW/installation.htm) + +### Windows Subsystem for Linux (WSL) + +This is what I (Anthony) personally use, although it may be more difficult to properly set up. + +[VSCode Docs for WSL](https://code.visualstudio.com/docs/cpp/config-wsl) (difficult for beginners) + +If you want to code in (neo)vim, you can install WSL and code through WSL bash. + +- Note that WSL has a max stack size of 64MB; I am unsure if this limitation is resolved yet. +- Note that WSL/vim clipboard integration is imperfect. + +## On Mac + +[Clang](https://en.wikipedia.org/wiki/Clang) is the default compiler for Mac OS X, but you should use [GCC](https://en.wikipedia.org/wiki/GNU_Compiler_Collection)'s `g++` since that's what [USACO](http://www.usaco.org/index.php?page=instructions) uses to compile your code. + +### Installation + +1. Open the **Terminal** application and familiarize yourself with some basic commands. + + <Resources> + <Resource + source="Jim Hoskins" + title="Intro to OS X Command Line" + url="https://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line" + starred + ></Resource> + <Resource + source="Rahul Saigal" + title="Mac Terminal Cheat Sheet" + url="https://www.makeuseof.com/tag/mac-terminal-commands-cheat-sheet/" + starred + ></Resource> + </Resources> + +2. Install XCode command line tools. + + ``` + xcode-select --install + ``` + + If you previously installed these you may need to update them: + + ``` + softwareupdate --list # list updates + softwareupdate -i -a # installs all updates + ``` + +3. Install [Homebrew](https://brew.sh/). + +4. Install `gcc` with Homebrew. + + ``` + brew install gcc + ``` + + According to [this](https://stackoverflow.com/questions/30998890/installing-opencv-with-brew-never-finishes) if `brew` doesn't seem to finish for a long time then + + ``` + brew install gcc --force-bottle + ``` + + probably suffices. + +5. You should be able to compile with `g++-#`, where # is the version number (ex. 9). Running the following command + + ``` + g++-9 --version + ``` + + should display something like this: + + ``` + g++-9 (Homebrew GCC 9.2.0_2) 9.2.0 + Copyright (C) 2019 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + ``` + +6. If you want to be able to compile with just `g++`, create a symbolic link as mentioned [here](https://stackoverflow.com/questions/28970935/osx-replace-gcc-version-4-2-1-with-4-9-installed-via-homebrew/28982564#28982564). + + ``` + cd /usr/local/bin + ln -fs g++-9 g++ + ``` + + `g++ --version` should now output the same thing as `g++-9 --version`. + +## Running With the Command Line + +Consider a simple program such as the following, which we'll save in `name.cpp`. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + int x; cin >> x; + cout << "FOUND " << x << "\n"; +} +``` + +It's not hard to [compile & run a C++ program](https://www.tutorialspoint.com/How-to-compile-and-run-the-Cplusplus-program). First, open up Powershell on Windows, Terminal on Mac, or your distro's terminal in Linux. We can compile `name.cpp` into an executable named `name` with the following command: + +``` +g++ name.cpp -o name +``` + +Then we can execute the program: + +``` +./name +``` + +If you type some integer and then press enter, then the program should produce output. We can write both of these commands in a single line: + +``` +g++ name.cpp -o name && ./name +``` + +### Redirecting Input & Output + +If you want to read input from `inp.txt` and write to `out.txt`, then use the following: + +``` +./name < inp.txt > out.txt +``` + +See [Input & Output](./io) for how to do file input and output within the program. + +### [Compiler Options (aka Flags)](https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html) + +Use [compiler flags](https://developers.redhat.com/blog/2018/03/21/compiler-and-linker-flags-gcc/) to change the way GCC compiles your code. Usually, we use something like the following in place of `g++ name.cpp -o name`: + +``` +g++ -std=c++17 -O2 name.cpp -o name -Wall +``` + +Explanation: + +- `-O2` tells `g++` to compile your code to run more quickly (see [here](https://www.rapidtables.com/code/linux/gcc/gcc-o.html)) +- `-std=c++17` allows you to use features that were added to C++ in 2017. USACO currently uses `-std=c++11`. +- `-Wall` checks your program for common errors. See [Debugging](./debugging) for more information. + +You should always compile with these flags. + +### Adding Shortcuts (Mac and Linux only) + +<Info title="Windows Users"> + +If you're on Windows, you can use an IDE to get these shortcuts, or you can install WSL (mentioned above). + +</Info> + +Of course, retyping the flags above can get tedious. You should define shortcuts so you don't need to type them every time! + +<Resources> + <Resource source="Jonathan Suh" url="https://jonsuh.com/blog/bash-command-line-shortcuts/" title="Aliases in Terminal" starred></Resource> +</Resources> + +Open your `.zshenv` with TextEdit: + +``` +open -a TextEdit ~/.zshenv +``` + +or some text editor (ex. sublime text with `subl`). + +``` +subl ~/.zshenv +``` + +You can add **aliases** and **functions** here, such as the following to compile and run C++. + +``` +co() { g++ -std=c++17 -O2 -o $1 $1.cpp -Wall -Wextra -Wshadow; } +run() { co $1 && ./$1 & fg; } +``` + +Now you can easily compile and run `name.cpp` from the command line with `co name && ./name` or `run name`. Note that all occurrences of `$1` in the function are replaced with `name`. + + +</CPPSection> + +<JavaSection> + +First, download the [JDK](https://docs.oracle.com/en/java/javase/14/install/overview-jdk-installation.html#GUID-8677A77F-231A-40F7-98B9-1FD0B48C346A). Then, test that you can use the right commands. + +On Windows, open `cmd` and type the following command into the prompt: + +``` +java +``` + +If you get the below result, you may have to add the JDK to your [PATH](https://docs.oracle.com/en/java/javase/14/install/installation-jdk-microsoft-windows-platforms.html#GUID-A7E27B90-A28D-4237-9383-A58B416071CA) variable. + +``` +'java' is not recognized as an internal or external command, +operable program or batch file +``` + +Otherwise, you're probably good to go. + +<IncompleteSection> + +What do you have to do on Mac to get Java working the command line? + +</IncompleteSection> + +Running a Java file off of the command-line is relatively simple after the JDK is downloaded. + +Consider this code of `Main.java` and assume it is in a file on your computer: + +```java +public class Main { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} +``` + +Use the `cd` command on your console to get to the directory that contains `Main.java`. Then, run the following command to compile the code: +``` +javac Main.java +``` +If it runs successfully, a file called `Main.class` will show up in the same directory, which can be executed with the next command: +``` +java Main +``` +If everything goes accordingly, the console should output `Hello World!`. + +If you do USACO-style file I/O, meaning that files are in the "local directory", then the files must be located in the same directory as the source code (if you use the above method). + +</JavaSection> + +<PySection> + +Download Python through [the official website](https://python.org). On Windows and Mac, you can download executable files directly; on Linux, you must either download the Python source code and compile from source, or obtain Python through your package manager. + +Make sure that you are using the correct version of Python. Python 2 is quite different from Python 3 (but parts of the version number beyond 2. or 3. do not matter much). We generally recommend newcomers to use Python 3, but if you are used to programming in Python 2, that is OK too. + +**The Python version can matter when using the command line to run Python;** sometimes, `python3` must be used instead of `python` in order to run Python 3. Try both commands, and pay attention to the version that Python prints, to determine if this is the case on your system. For example, running the following in Terminal on my computer produces: + +``` +python --version # prints Python 2.7.13 +python3 --version # prints Python 3.8.1 +``` + +</PySection> + +</LanguageSection> + +# Using an IDE + +<Resources> + <Resource source="IOI" title="2019 Contest Environment" url="https://ioi2019.az/en-content-26.html">software you can use at IOI</Resource> +</Resources> +<LanguageSection> + +<CPPSection> + +These often have C++ support already built-in. + +<Resources> + <Resource + source="Microsoft" title="Visual Studio Code" + url="https://code.visualstudio.com/" + starred + > + More lightweight than Visual Studio, requires some configuration. See{' '} + <a href="http://www.csc.kth.se/~jsannemo/slask/main.pdf">PAPC 2.1</a> for + setup instructions. + </Resource> + <Resource source="Geany" title="Geany" url="https://www.geany.org/" starred> + Lightweight, frequently used at IOI. + </Resource> + <Resource source="Code::Blocks" title="Code::Blocks" url="http://www.codeblocks.org/"> + Bad on Mac, <a href="https://codeforces.com/blog/entry/61780">apparently</a> unstable at IOI. + </Resource> + <Resource source="Apple" title="XCode" url="https://developer.apple.com/xcode/"> + Mac only. + </Resource> + <Resource source="Jetbrains" title="CLion" url="https://www.jetbrains.com/clion/"> + Requires a license, but{' '} + <a href="https://www.jetbrains.com/community/education/#students"> + free for students + </a> + . + </Resource> +</Resources> + +<!-- <Resource source="Microsoft" title="Visual Studio" url="https://visualstudio.microsoft.com/vs/"> + Heavier cousin of VS Code. VS Code is better for competitive programming. + </Resource> --> + +</CPPSection> + +<JavaSection> + +It can be useful to use a Java IDE to take advantage of the powerful debugging features in Java. + +<Resources> + <Resource source="Jetbrains" title="IntelliJ IDEA" url="https://www.jetbrains.com/idea/"> + costs money but is{' '} + <a href="https://www.jetbrains.com/community/education/#students"> + free for students + </a> + . + </Resource> + <Resource source="Eclipse" title="IDE" url="https://www.eclipse.org/eclipseide/"> + Freely available + </Resource> +</Resources> + +</JavaSection> + +<PySection> + +Python comes with a program, known as **IDLE** (Integrated Development and Learning Environment), that allows one to write and run Python code. It supports many features, such as syntax highlighting and automatic indentation, that one would normally expect from an IDE/text editor. However, feel free to use another editor or IDE if you want to. + +</PySection> + +</LanguageSection> diff --git a/content/1_Intro/Running_Cpp.md b/content/1_Intro/Running_Cpp.md deleted file mode 100644 index dfe2176..0000000 --- a/content/1_Intro/Running_Cpp.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -id: running-cpp -title: Running C++ -author: Nathan Wang, Benjamin Qi, Anthony Wang -description: Options for running C++ both online and locally. ---- - -Please let us know if these installation instructions do not work for you. - -# Using C++ Online - - - [OnlineGDB](https://www.onlinegdb.com/) - - online compiler with an embedded GDB debugger - - can be buggy sometimes - - supports files and file I/O - - [CSAcademy](https://csacademy.com/workspace/) - - pretty nice (unless you get "Estimated Queue Time: ...") - - "saved locally" will not save your code if you close the tab, press Command-S to save. - - [Ideone](http://ideone.com/) - - okay ... has the bare minimum you need for running C++ - - make sure your code is not public - - sometimes erases your code when you first create it (so get in the habit of copying your code first) - -You can share code with [pastebin](https://pastebin.com/) or [hastebin](https://hastebin.com/). - -# Using an IDE - -These often have C++ support already built-in. - - - [Visual Studio Code](https://code.visualstudio.com/) - - lightweight, fast IDE, but requires some configuration - - see [PAPC Ch 2.1](http://www.csc.kth.se/~jsannemo/slask/main.pdf) for setup instructions - - [Visual Studio](https://visualstudio.microsoft.com/vs/) - - heavier cousin of VS Code, VS Code is better for competitive programming - - [Geany](https://www.geany.org/) - - Ben: I used at IOI - - [Codeblocks](http://www.codeblocks.org/) - - bad on Mac - - [XCode](https://developer.apple.com/xcode/) - - Mac only - - [CLion](https://www.jetbrains.com/clion/) - - requires a license, but [free for students](https://www.jetbrains.com/community/education/#students) - -# Using Command Line - -Alternatively, run C++ from the command line and use a text editor of your choice. - -## Text Editors - - - [Sublime Text 3](https://www.sublimetext.com/) - - fast, lightweight text editor for Windows, Mac, and Linux - - [Editing Build Settings](https://stackoverflow.com/questions/23789410/how-to-edit-sublime-text-build-settings) - - no need to do this if you just use command line to compile & run - - [FastOlympicCoding Addon](https://github.com/Jatana/FastOlympicCoding) - - see "Debugging" for another way to stress test - - [Sublime Snippets](https://www.granneman.com/webdev/editors/sublime-text/top-features-of-sublime-text/quickly-insert-text-and-code-with-sublime-text-snippets) - - Ben - I use to insert templates - - [Symlink](https://www.sublimetext.com/docs/3/osx_command_line.html) - - Ben - Using `/usr/local/bin/subl` instead of `~/bin/subl` worked for me on OS X Mojave. - - [Atom](https://atom.io/) - - another text editor for Windows, Mac, and Linux from the makers of Github - - [Vim](https://www.vim.org/) - - classic text editor, usually preinstalled on Mac and Linux, and also available for Windows - - probably easiest way to print syntax-highlighted code on Mac, see the response to [this post](https://stackoverflow.com/questions/1656914/printing-code-with-syntax-highlighting) - -## On Linux - -GCC is usually preinstalled on most Linux distros. You can check if it is installed with - -``` -whereis g++ -``` - -If it is not preinstalled, you can probably install it using your distro's package manager. - -## On Windows - -### MinGW - -One option is [MinGW](http://mingw.org/). - -First, download and run the [MinGW installer](https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/). Once it's installed, open the MinGW Installation Manager, click on Basic Setup on the left, and select `mingw32-gcc-g++-bin` for installation. - -[Adding MinGW to PATH](https://www.rose-hulman.edu/class/csse/resources/MinGW/installation.htm) - -### WSL - -Another good option is Windows Subsystem for Linux (WSL) which is what I (Anthony) personally use, although it may be more difficult to properly set up. - -[VSCode Docs for WSL](https://code.visualstudio.com/docs/cpp/config-wsl) (difficult for beginners) - -Nathan Wang: If you want to code in (neo)vim, you can install WSL and code through WSL bash. - -- Note that WSL has a max stack size of 64MB; I am unsure if this limitation is resolved yet. -- Note that WSL/vim clipboard integration is imperfect. - -## On Mac - -[Clang](https://en.wikipedia.org/wiki/Clang) is the default compiler for Mac OS X, but you should use [GCC](https://en.wikipedia.org/wiki/GNU_Compiler_Collection)'s g++ since that's what [USACO](http://www.usaco.org/index.php?page=instructions) uses to compile your code. - -### Installation - - 1. Open the **Terminal** application and familiarize yourself with some basic commands. - - - [Intro to OS X Command Line](https://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line). - - [Mac Terminal Cheat Sheet](https://www.makeuseof.com/tag/mac-terminal-commands-cheat-sheet/) - - 2. Install XCode command line tools. - - ``` - xcode-select --install - ``` - - If you previously installed these you may need to update them: - - ``` - softwareupdate --list # list updates - softwareupdate -i -a # installs all updates - ``` - - 3. Install [Homebrew](https://brew.sh/). - - 4. Install gcc with Homebrew. - - ``` - brew install gcc - ``` - - According to [this](https://stackoverflow.com/questions/30998890/installing-opencv-with-brew-never-finishes) if `brew` doesn't seem to finish for a long time then - - ``` - brew install gcc --force-bottle - ``` - - probably suffices. - - 5. You should be able to compile with `g++` or maybe `g++-#`, where # is the version number (ex. 9). Running the following command - - ``` - g++-9 --version - ``` - - should display something like this: - - ``` - g++-9 (Homebrew GCC 9.2.0_2) 9.2.0 - Copyright (C) 2019 Free Software Foundation, Inc. - This is free software; see the source for copying conditions. There is NO - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - ``` - -## Running With the Command Line - -Consider a simple program such as the following, which we'll save in `name.cpp`. - -```cpp -#include <bits/stdc++.h> -using namespace std; - -int main() { - int x; cin >> x; - cout << "FOUND " << x << "\n"; -} -``` - -It's not hard to [compile & run a C++ program](https://www.tutorialspoint.com/How-to-compile-and-run-the-Cplusplus-program). First, open up Powershell on Windows, Terminal on Mac, or your distro's terminal in Linux. We can compile `name.cpp` into an executable named `name` with the following command: - -``` -g++ name.cpp -o name -``` - -Then we can execute the program: - -``` -./name -``` - -If you type some integer and then press enter, then the program should produce output. We can write both of these commands in a single line: - -``` -g++ name.cpp -o name && ./name -``` - -### Redirecting Input & Output - -If you want to read input from `inp.txt` and write to `out.txt`, then use the following: - -``` -./name < inp.txt > out.txt -``` - -See "Intro - Introductory Problems" for how to do file input and output within the program. - -### Compiler Options (aka Flags) - -Use [compiler flags](https://developers.redhat.com/blog/2018/03/21/compiler-and-linker-flags-gcc/) to change the way GCC compiles your code. Usually, we use the following in place of `g++ name.cpp -o name`: - -`g++ -std=c++17 -O2 name.cpp -o name -Wall -Wextra -Wshadow` - -Explanation: - - - `-O2` tells `g++` to compile your code to run more quickly (see [here](https://www.rapidtables.com/code/linux/gcc/gcc-o.html)) - - `-std=c++17` allows you to use features that were added to C++ in 2017. USACO currently uses `-std=c++11`. - - `-Wall -Wextra -Wshadow` checks your program for common errors. See "General - Debugging" for more information. - - [Summary of Options](https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html) - -You should always compile with these flags. - -### Adding Shortcuts (Mac and Linux only) - -(alternatives for Windows?) - -Of course, retyping the flags above can get tedious. You should define shortcuts so you don't need to type them every time! - -[Aliases in Terminal](https://jonsuh.com/blog/bash-command-line-shortcuts/) - -Open your bash profile with a text editor such as gedit (or sublime text). - -``` -brew install gedit -gedit ~/.zshenv -``` - -You can add **aliases** and **functions** here, such as the following to compile and run C++. - -``` -co() { g++ -std=c++17 -O2 -o $1 $1.cpp -Wall -Wextra -Wshadow; } -run() { co $1 && ./$1 & fg; } -``` - -Now you can easily compile and run `name.cpp` from the command line with `co name && ./name` or `run name`. Note that all occurrences of `$1` in the function are replaced with `name`. diff --git a/content/1_Intro/Tips.mdx b/content/1_Intro/Tips.mdx new file mode 100644 index 0000000..0ecb911 --- /dev/null +++ b/content/1_Intro/Tips.mdx @@ -0,0 +1,119 @@ +--- +id: cpp-tips +title: C++ Tips & Tricks +author: Benjamin Qi +description: "Explaining various parts of Ben's template." +--- + +## `#include <bits/stdc++.h>` + +Use in place of separately including libraries. + +<Resources> + <Resource source="CPH" title="1.1 - C++ Code Template" starred></Resource> + <Resource source="GFG" title="GFG - <bits/stdc++.h>" url="bitsstdc-h-c"> </Resource> +</Resources> + +## Macros + +<Resources> + <Resource source="CPH" title="1.4 - Shortening Code" starred></Resource> + <Resource source="GFG" url="cc-preprocessors" title="Macros"></Resource> + <Resource + source="GCC" + url="https://gcc.gnu.org/onlinedocs/cpp/Macros.html" + title="Online Docs - Macros" + > + reference + </Resource> +</Resources> + +## `typedef`, `using` + +[Quora](https://www.quora.com/What-is-the-difference-between-typedef-and-using-in-C++) + +## Template + +[My Template](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/template.cpp) + +### Pairs + +```cpp +typedef pair<int,int> pi; +#define mp make_pair +#define f first +#define s second +``` + +Pretty annoying to keep typing `first` and `second` (especially if you have nested pairs ...) + +### Vectors + +```cpp +typedef vector<int> vi; + +#define sz(x) (int)x.size() +#define all(x) begin(x), end(x) +``` + +It's generally a good idea to convert a size to a signed integer before doing anything with it to avoid cases like the following. + +```cpp +vi x; +cout << x.size()-1 << "\n"; // 18446744073709551615 +cout << sz(x)-1 << "\n"; // -1 +``` + +`all(v)` makes sorting part or all of a vector a bit shorter. + +```cpp +vi v = {2,4,1,5,3}; +sort(1+all(v)); // {2,1,3,4,5} +sort(all(v)); // {1,2,3,4,5} +``` + +### Constants + +```cpp +const int MOD = 1e9+7; // 998244353; +const int MX = 2e5+5; +const ll INF = 1e18; +const ld PI = acos((ld)-1); +``` + +$10^9+7$ is a prime that appears quite frequently in programming contests. Interestingly, $(10^9+7-1)/2=5\cdot 10^8+3$ is also prime. On the other hand, $998244353-1$ is divisible by $2^{23}$, which is useful for [NTT](<https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general)#Number-theoretic_transform>). + +Almost all USACO problems satisfy $N\le 2\cdot 10^5$, so that's what `MX` is for. + +### Generating Random Numbers + +```cpp +mt19937 rng((uint32_t)chrono::steady_clock::now().time_since_epoch().count()); +``` + +See [neal's blog](https://codeforces.com/blog/entry/61587) about why `rand()` is bad. Use `rng()` instead. + +### Generating Sublime Snippets + +[Snippets](https://www.granneman.com/webdev/editors/sublime-text/top-features-of-sublime-text/quickly-insert-text-and-code-with-sublime-text-snippets) + +I use [this python script](https://github.com/bqi343/USACO/blob/master/Contests/Tools/Other/makeSnip.py) to automatically update snippets for all of my templates. + +Instructions (Mac): + + 1. Install `python3`. + 2. Clone [my USACO repository](https://github.com/bqi343/USACO) to `~/Documents/USACO`. + 3. Add the following to your `~/.zshenv`. + + ``` + export USACO=~/Documents/USACO + makeSnip() { + cd $USACO/Implementations/content/contest + python3 genShort.py + python3 genLong.py + python3 $USACO/Contests/Tools/Other/makeSnip.py + } + ``` + + 4. Run `makeSnip` from terminal. It should display a list of the templates that will be added as snippets. + 5. If this succeeded, then typing `Temp` in a cpp file will automatically load my template!
\ No newline at end of file diff --git a/content/1_Intro/USACO Contest Strategy.pdf b/content/1_Intro/USACO Contest Strategy.pdf Binary files differnew file mode 100644 index 0000000..a837a61 --- /dev/null +++ b/content/1_Intro/USACO Contest Strategy.pdf diff --git a/content/1_Intro/USACO_Camp.mdx b/content/1_Intro/USACO_Camp.mdx new file mode 100644 index 0000000..f421a62 --- /dev/null +++ b/content/1_Intro/USACO_Camp.mdx @@ -0,0 +1,62 @@ +--- +id: usaco-camp +title: USACO Camp +author: Benjamin Qi +description: "Relevant Quora links (and more) regarding camp qualification." +--- + +<Resources> + <Resource source="Quora" title="What is it like to attend USACO Camp?" url="https://www.quora.com/What-is-it-like-to-attend-the-USACO-training-camp" starred> </Resource> +</Resources> + +Each of the \~26 finalists is either a **Holstein** or a **Guernsey**. + + - Holsteins (1/3 to 1/2 of all finalists) + - can include first-time finalists + - six contests ~~(fun!!)~~ + - Guernseys + - no seniors or past finalists + - lectures, two contests + +Contests are 3-5 hrs with 3-4 problems each. <!-- ~~and are frequently extended for lunch breaks.~~ --> + +## Selection + +<Resources> + <Resource source="USACO" title="The Road to the IOI Team" url="http://www.usaco.org/index.php?page=contests" starred>16..24 students is no longer accurate</Resource> + <Resource source="Quora" title="How do I get into USACO Camp?" url="https://www.quora.com/How-do-I-get-into-the-USACO-training-camp" starred>See Brian's response.</Resource> + <Resource source="Quora" title="Is USACO Training Progress used in the selection of USACO Finalists?" url="https://www.quora.com/Is-progress-in-the-USACO-Training-Gateway-used-in-the-selection-of-USACO-finalists">nice we have conflicting answers</Resource> + <Resource source="Jelani Nelson" title="2011-20 USACO Camp Unique Participants by State" url="https://tinyurl.com/usacomap">what an amazing distribution</Resource> +</Resources> + +<Optional title="excerpt from 'How do I get into USACO Camp?'"> + +> A few answers mention how to train, so I won't focus on that (I didn't train). + +... + +> But even I made it to camp. So you'll have no problem. + +</Optional> + +Campers generally average at least one problem and some (?) partial credit per contest. + +<Info title="Pro Tip"> + +Submitting all the sample cases ~~might increase your total score by half a problem across the whole season!!~~ + +</Info> + +### First-time campers by grade level + +<center> + +| | <9 | 9 | 10 | 11 | 12 | +|----|----|---|----|----|----| +| 16 | 0 | 4 | 10 | 4 | 1 | +| 17 | 1 | 1 | 11 | 6 | 2 | +| 18 | 1 | 1 | 5 | 7 | 0 | +| 19 | 0 | 3 | 9 | 4 | 2 | +| 20 | 0 | 1 | 4 | 8 | 2 | + +</center> diff --git a/content/1_Intro/Using_This_Guide.mdx b/content/1_Intro/Using_This_Guide.mdx new file mode 100644 index 0000000..8f8eb3f --- /dev/null +++ b/content/1_Intro/Using_This_Guide.mdx @@ -0,0 +1,97 @@ +--- +id: using-this-guide +title: Using This Guide +author: Nathan Wang, Benjamin Qi +description: How to effectively use this guide to maximize your productivity. +--- + +**Pre-Release Notice**: This guide is in the _pre-release_ stage, meaning it is not yet complete! + +## Contact Us + +Please help us complete this guide by **giving feedback** using the "Contact Us" button<Asterisk>It's located on the bottom left of the screen. If you can't see it, open the hamburger menu by clicking the icon on the top left of the screen.</Asterisk> or clicking the Feedback icon on the bottom right of the screen. You can give feedback about anything! + +If you found the guide useful, or if you got stuck while using the guide, please also let us know! :) + +## About This Guide + +<Warning title="This guide is NOT a syllabus!!"> + +Topics on this guide reflect _past_ problems, not _future_ problems. Contest problems may contain topics that aren't mentioned in this guide, and topics that appear in one division of this guide may appear in lower divisions in future contests. + +</Warning> + + - For Bronze, Silver, and Gold contestants, we aim to be a "**one stop shop**," meaning that this is the only site you have to use to be exposed to most (if not all) of the topics required for Bronze - Gold.<Asterisk>Of course, you should still use other websites as necessary.</Asterisk> + - For Platinum contestants, we'll try our best to cover the main topics. + - The "Advanced" section contains material that is relevant for USACO Camp and beyond. + +**Note:** We are currently focusing on the Intro - Gold sections. Modules that are part of Platinum and Advanced will remain mostly incomplete for the time being. + +<!-- <Asterisk>There are too many topics for us to effectively cover all of them. If you want to do well in Platinum, you will have to find additional resources on your own in addition to this site</Asterisk> --> + +## How to Use This Guide + + - Use the **Module Progress** dropdowns (to the right of the module title and at the bottom of the page) to track your progress through this guide! Changes will be reflected by the navigation bar on the left. + - If you're stuck, **get help** using the "Contact Us" button on the lower left (or in the hamburger menu on smaller screens). + - Read through all starred resources before continuing! + - We highly recommend doing all starred problems. If you need more practice, try some of the unstarred ones. + - Difficulty represents how challenging a problem is expected to be to someone after they read through the module, and is **not** comparable across modules. + - See [Introducing Modules](./modules) for more information. + +<Warning> + +A lot of the resource and problem lists have issues (inaccurate difficulty levels, not enough or too many starred, etc). Again, use "Contact Us" as necessary. + +</Warning> + +**Skipping around** is especially recommended for higher level contestants (Gold/Platinum). Feel free to <TextTooltip content="You can do this by scrolling to the end of a module">mark a module</TextTooltip> as "Skipped" and come back to it at a later time! + +For lower level contestants, the guide is generally<Asterisk>There are some exceptions; for example, the last module in "Silver -> Sorting" assumes knowledge of the first two modules in "Silver -> Ordered Sets & Maps." In any case, we will list prerequisites to each module as needed.</Asterisk> designed to be completed in order. However, feel free to skip certain modules and come back to it later. + +## Changing Your Language + +To change your language, click the "Language" button on the bottom left corner of the screen (or in the hamburger menu, depending on the size of the window). Content may be different depending on which language is selected! + +<LanguageSection> + +<CPPSection> + +Your current language is **C++**. + +</CPPSection> + +<JavaSection> + +Your current language is **Java**. + +<Warning> + +Currently very limited support for languages other than C++. + +</Warning> + +</JavaSection> + +<PySection> + +Your current language is **Python**. + +<Warning> + +Currently very limited support for languages other than C++. + +</Warning> + +</PySection> + +</LanguageSection> + +## For Contributors + +We welcome any and all contributions to this site! Please reach out to the guide coordinator, [Nathan Wang](mailto:nathan.r.wang@gmail.com), to get started. + +## For Instructors + +If you're an instructor/club officer teaching for _free_, please feel free to use whatever parts you'd like from this guide, though please provide credit + a link to this site! Additionally, feel free to [reach out to us](mailto:nathan.r.wang@gmail.com) if you have a specific request for what material you'd find helpful. + +_Licensing notes_: No part of this site may be used,<Asterisk>Exceptions exist (generally for free classes); see the full license for more details</Asterisk> reproduced, redistributed, commercialized, or sold without prior written permission. [Learn more here.](/license)
\ No newline at end of file diff --git a/content/1_Intro/finalist.py b/content/1_Intro/finalist.py new file mode 100644 index 0000000..a4cd9cf --- /dev/null +++ b/content/1_Intro/finalist.py @@ -0,0 +1,53 @@ +from contextlib import closing +from bs4 import BeautifulSoup +import urllib.request +import time +import sys +import os +import pprint + +def parse(url): + page = urllib.request.urlopen(url) + return BeautifulSoup(page,'html.parser') + +prefix="http://www.usaco.org/index.php?page=finalists" + +past = set() + +def nor(x): + return " ".join(x.split()) + +for i in range(12,21): + html = parse(prefix+str(i)) + for a in html.find_all('table'): + par = [] + for b in a.find_all('tr'): + arr = [] + for c in b.find_all('td'): + arr.append(nor(c.text)) + if len(arr) > 1: + par.append((arr[0],arr[1])) + nex = [0 for i in range(5)] + pre = 0 + tot = 0 + new = [] + for a in par: + # print(a) + rem = int(a[0])-i-2000 + # print(rem) + tot += 1 + if a in past: + pre += 1 + else: + new.append(a) + nex[4-min(rem,4)] += 1 + past.add(a) + if i >= 16: + # print(tot) + # print(pre,nex) + print(nex) + # for t in nex: + # print(t,end=", ") + # print() + # for f in new: + # print(f) diff --git a/content/3_Bronze/0_Bronze_Overview.md b/content/2_Bronze/0_Bronze_Overview.md index e98859d..a96d58d 100644 --- a/content/3_Bronze/0_Bronze_Overview.md +++ b/content/2_Bronze/0_Bronze_Overview.md @@ -7,4 +7,4 @@ author: Brian Dean Todo: - Brian's advice for new bronze competitors - Potentially a video of select concepts that are most important for bronze problems - - in general, words of advice/wisdom
\ No newline at end of file + - in general, words of advice/wisdom diff --git a/content/2_Bronze/Ad_Hoc.mdx b/content/2_Bronze/Ad_Hoc.mdx new file mode 100644 index 0000000..c7429f6 --- /dev/null +++ b/content/2_Bronze/Ad_Hoc.mdx @@ -0,0 +1,42 @@ +--- +id: ad-hoc +title: Ad Hoc Problems +author: Michael Cao +description: "Problems that don't fall under any well-defined category." +frequency: 1 +--- +import { Problem } from "../models"; + +export const problems = { + general: [ + new Problem("Bronze", "Sleepy Cow Sorting", "892", "Hard", false, [], ""), + new Problem("Bronze", "Hoofball", "808", "Hard", false, [], ""), + new Problem("Bronze", "Taming the Herd", "809", "Hard", false, [], ""), + ], +}; + +Most bronze problems fall into specific categories, such as simulation and complete search. Those which don't often require more thought, although they are not necessarily more difficult to implement. + +Any problem that doesn't fall under any well-defined category can be labelled as **Ad Hoc**. Since there is no fixed algorithm or idea to solving these problems, they can be hard to generalize. In this module, we'll go over some general tips that may be useful in approaching these problems. + +<ul> + <li> Draw lots of small cases to gain a better understanding of the problem. If you're having trouble debugging, draw more cases. If you don't know how to start with a problem, draw more cases. Whenever you don't know how to further approach a problem, you're probably missing an important observation, so draw more cases and make observations about properties of the problem. </li> + <li> Whenever you find an observation that seems useful, write it down! Writing down ideas lets you easily come back to them later, and makes sure you don't forget about ideas that could potentially be the solution. </li> + <li> Don't get stuck on any specific idea, unless you see an entire solution. Trying to complete search an Ad Hoc problem could end up wasting a lot of your time in contest. </li> + <li> Try to approach the problem from a lot of different perspectives. Try to draw a visual depiction of the problem, mess around with formulas, or draw a graph (see Graph Theory module). One of the most helpful things you can do when solving Ad Hoc problems is to keep trying ideas until you make progress. This is something you get better at as you do more problems.</li> +</ul> + +These tips are helpful in solving Ad Hoc problems. However, in the end, the best way to get better at Ad Hoc problems (or any type of problem) is to do a lot of them. Try to solve as many practice problems below as you can, and click the solution sketch tab if you can't figure the solution out. + +<!-- If anyone disagrees with any of the tips / ideas presented in the blogs below, just let me know. I think they're quite interesting, though I haven't tried it out myself. --> + +<Resources> + <Resource source="ZLE" title="The Art of Thinking" url="https://kauntaofficial.github.io/the-art-of-thinking">blog with more tips</Resource> + <Resource source="ZLE" title="Developing Intuition" url="https://kauntaofficial.github.io/developing-intuition">developing intuition while practicing</Resource> +</Resources> + +## Problems + +Dhruv Rohatgi problems!! + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/2_Bronze/Complete_Rec.mdx b/content/2_Bronze/Complete_Rec.mdx new file mode 100644 index 0000000..3dd1aa9 --- /dev/null +++ b/content/2_Bronze/Complete_Rec.mdx @@ -0,0 +1,159 @@ +--- +id: complete-rec +title: "Complete Search with Recursion" +author: Darren Yao, Sam Zhang +description: "Includes generating subsets and permutations." +frequency: 2 +prerequisites: + - intro-complete +--- + +import { Problem } from "../models"; + +export const problems = { + subset: [ + new Problem("CSES", "Apple Division", "1623", "Intro|Very Easy", false, ["Recursion", "Subsets"], "all $2^n$ subsets"), + ], + permSam: [ + new Problem("CSES", "Creating Strings I", "1622", "Intro|Easy", false, [], "all perms of string"), + ], + ex: [ + new Problem("Bronze", "Photoshoot", "988", "Normal", false, [], ""), + ], + back: [ + new Problem("CSES", "Chessboard and Queens", "1624", "Normal", false, [], "Recursively backtrack. See CSES book for more details."), + new Problem("CSES", "Grid Paths", "1625", "Insane", false, [], "Recursively backtrack. See CSES book for more details."), + ], + gen: [ + new Problem("Bronze", "Back & Forth", "857", "Hard", false, [], "exponential time going through all possibilities"), // 99.63 + new Problem("Bronze", "Livestock Lineup", "965", "Hard", false, ["permutations"], ""), // 91.95 + ], +}; + +## Generating Subsets + +<Resources> + <Resource source="CPH" title="5.1 - Generating Subsets" starred> </Resource> +</Resources> + +<Problems problems={problems.subset} /> + +## Generating Permutations + +<Resources> + <Resource source="CPH" title="5.2 - Generating Permutations" starred> </Resource> +</Resources> + +<Problems problems={problems.permSam} /> + +<br /> + +A **permutation** is a reordering of a list of elements. Some problems will ask for an ordering of elements that satisfies certain conditions. In these problems, if $N \leq 10$, we can probably iterate through all permutations and check each permutation for validity. For a list of $N$ elements, there are $N!$ ways to permute them, and generally we'll need to read through each permutation once to check its validity, for a time complexity of $O(N \cdot N!)$. + +<Resources> + <Resource source="GFG" url="write-a-c-program-to-print-all-permutations-of-a-given-string" title="Printing all Permutations of Given String"> </Resource> +</Resources> + +### Lexicographical Order + +This term is mentioned quite frequently: + +<Problems problems={problems.ex} /> + +Think about how are words ordered in a dictionary. (In fact, this is where the term "lexicographical" comes from.) + +In dictionaries, you will see that words beginning with the letter 'a' appears at the very beginning, followed by words beginning with 'b', and so on. If two words have the same starting letter, the second letter is used to compare them; if both the first and second letters are the same, then use the third letter to compare them, and so on until we either reach a letter that is different, or we reach the end of some word (in this case, the shorter word goes first). + +Permutations can be placed into lexicographical order in almost the same way. We first group permutations by their first element; if the first element of two permutations are equal, then we compare them by the second element; if the second element is also equal, then we compare by the third element, and so on. + +For example, the permutations of 3 elements, in lexicographical order, are + +$$ +[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]. +$$ + +Notice that the list starts with permutations beginning with 1 (just like a dictionary that starts with words beginning with 'a'), followed by those beginning with 2 and those beginning with 3. Within the same starting element, the second element is used to make comparisions. + +Generally, unless you are specifically asked to find the lexicographically smallest/largest solution, you do not need to worry about whether permutations are being generated in lexicographical order. However, the idea of lexicographical order does appear quite often in programming contest problems, and in a variety of contexts, so it is strongly recommended that you familiarize yourself with its definition. + +### Checking Permutations in Lexicographical Order + +What's going to be in the `check` function depends on the problem, but it should verify whether the current permutation satisfies the constraints given in the problem. + +<LanguageSection> + +<CPPSection> + +#### Method 1 + +($O(N\cdot N!)$ code) + +<IncompleteSection /> + +#### Method 2 + +<Resources> + <Resource source="Mark Nelson" title="Next Permutation" url="https://marknelson.us/posts/2002/03/01/next-permutation.html"> </Resource> +</Resources> + +Alternatively, we can just use the [`next_permutation()`](https://en.cppreference.com/w/cpp/algorithm/next_permutation) function. This function takes in a range and modifies it to the next greater permutation. If there is no greater permutation, it returns false. To iterate through all permutations, place it inside a `do-while` loop. We are using a `do-while` loop here instead of a typical `while` loop because a `while` loop would modify the smallest permutation before we got a chance to process it. + +```cpp +do { + check(v); // process or check the current permutation for validity +} while(next_permutation(v.begin(), v.end())); +``` + +Each call to `next_permutation` makes a constant number of swaps on average if we go through all $N!$ permutations of size $N$. + +</CPPSection> + +<JavaSection> + +```java +import java.util.*; + +public class Test { + static boolean[] used; + static ArrayList<Integer> cur = new ArrayList<Integer>(); + static int n; + static void gen() { + if (cur.size() == n) { + check(cur); // check current permutation for validity, or print it, etc. + return; + } + for (int i = 0; i < n; ++i) if (!used[i]) { + used[i] = true; cur.add(i+1); + gen(); + used[i] = false; cur.remove(cur.size()-1); + } + } + static void genPerm(int _n) { + n = _n; used = new boolean[n]; + gen(); + } + public static void main(String[] Args) { + genPerm(5); + } +} +``` + +</JavaSection> + +</LanguageSection> + +## Backtracking + +<Problems problems={problems.back} /> + +<Resources> + <Resource source="CPH" title="5.3, 5.4 - Backtracking, Pruning"> </Resource> +</Resources> + +No (current) bronze problem should require pruning ... + +## Problems + +<Problems problems={problems.gen} /> + +<IncompleteSection />
\ No newline at end of file diff --git a/content/2_Bronze/Intro_Complete.mdx b/content/2_Bronze/Intro_Complete.mdx new file mode 100644 index 0000000..8b21022 --- /dev/null +++ b/content/2_Bronze/Intro_Complete.mdx @@ -0,0 +1,109 @@ +--- +id: intro-complete +title: "Introduction to Complete Search" +author: Darren Yao +description: "A basic example: iterating through all pairs." +frequency: 4 +--- + +import { Problem } from "../models"; + +export const problems = { + probs: [ + new Problem("Bronze", "Diamond Collector", "639", "Easy", false, ["Nested Loop"], "fix the min"), // 99.9 + new Problem("Bronze", "Milk Pails", "615", "Easy", false, ["Nested Loop"], "Hint: Fix the number of first-type operations you perform."), // 99.71 + new Problem("Bronze", "Bovine Genomics", "736", "Normal", false, ["Nested Loop"], ""), // 99.73 + new Problem("Bronze", "Cow Gymnastics", "963", "Normal", false, ["Nested Loop"], "Hint: Brute force over all possible pairs."), // 99.93 + new Problem("Bronze", "Where Am I?", "964", "Normal", false, [], "Hint: Brute force over all possible substrings."), // 99.48 + new Problem("Bronze", "Triangles", "1011", "Normal", false, [], "loop over the possible right angles"), // 98.02 + new Problem("Bronze", "Lifeguards", "784", "Normal", false, [], "Hint: Try removing each lifeguard one at a time."), // 98.76 + new Problem("Bronze", "Circle Cross", "712", "Hard", false, [], "For every pair, check if they cross (draw small cases to figure out how). Be careful about overcounting."), // 89.6 + new Problem("Bronze", "Load Balancing", "617", "Very Hard", false, [], ""), // 82.79 + new Problem("Bronze", "Bull in a China Shop", "640", "Very Hard", false, [], "lots of WA on this one"), // 47.38 + ], +}; + +<!-- new Problem("Bronze", "Field Reduction", "641", "Hard", false, [], "Hint: For this problem, you can't do a full complete search; you have to do a reduced search."), // 91.75 --> + +<!-- new Problem("Silver", "Field Reduction", "642", "Very Hard", false, [], ""), --> + +<Resources> + <Resource source="IUSACO" title="6 - Complete Search">module is based off this</Resource> +</Resources> + +<br /> + +In many problems (especially in Bronze) it suffices to check all possible cases in the solution space, whether it be all elements, all pairs of elements, or all subsets, or all permutations. Unsurprisingly, this is called **complete search** (or **brute force**), because it completely searches the entire solution space. + +## Example + +You are given $N$ $(3 \leq N \leq 5000)$ integer points on the coordinate plane. Find the square of the maximum Euclidean distance (aka length of the straight line) between any two of the points. + +### Input Format + +The first line contains an integer $N$. + +The second line contains $N$ integers, the $x$-coordinates of the points: $x_1, x_2, \dots, x_N$ ($-1000 \leq x_i \leq 1000$). + +The third line contains $N$ integers, the $y$-coordinates of the poitnts: $y_1, y_2, \dots, y_N$ ($-1000 \leq y_i \leq 1000$). + +### Output Format + +Print one integer, the square of the maximum Euclidean distance between any two of the points. + +<Spoiler title="Solution"> + +We can brute-force every pair of points and find the square of the distance between them, by squaring the formula for Euclidean distance: $\text{distance}^2 = (x_2-x_1)^2 + (y_2-y_1)^2$. Thus, we store the coordinates in arrays `X[]` and `Y[]`, such that `X[i]` and `Y[i]` are the $x$- and $y$-coordinates of the $i_{th}$ point, respectively. Then, we iterate through all possible pairs of points, using a variable max to store the maximum square of distance between any pair seen so far, and if the square of the distance between a pair is greater than our current maximum, we set our current maximum to it. + +<LanguageSection> + +<CPPSection> + +```cpp +int high = 0; // storing the current maximum +for(int i = 0; i < n; i++){ // for each first point + for(int j = i+1; j < n; j++){ // for each second point + int dx = x[i] - x[j]; + int dy = y[i] - y[j]; + high = max(high, dx*dx + dy*dy); + // if the square of the distance between the two points is greater than + // our current maximum, then update the maximum + } +} +cout << high << endl; +``` + +</CPPSection> + +<JavaSection> + +```java +int max = 0; // storing the current maximum +for(int i = 0; i < n; i++){ // for each first point + for(int j = i+1; j < n; j++){ // for each second point + int dx = x[i] - x[j]; + int dy = y[i] - y[j]; + max = Math.max(max, dx*dx + dy*dy); + // if the square of the distance between the two points is greater than + // our current maximum, then update the maximum + } +} +pw.println(max); +``` + +</JavaSection> + +</LanguageSection> + +<br /> + +A couple notes: + + - First, since we're iterating through all pairs of points, we start the $j$ loop from $j = i+1$ so that point $i$ and point $j$ are never the same point. Furthermore, it makes it so that each pair is only counted once. In this problem, it doesn't matter whether we double-count pairs or whether we allow $i$ and $j$ to be the same point, but in other problems where we're counting something rather than looking at the maximum, it's important to be careful that we don't overcount. + - Secondly, the problem asks for the square of the maximum Euclidean distance between any two points. Some students may be tempted to maintain the maximum distance in an integer variable, and then square it at the end when outputting. However, the problem here is that while the square of the distance between two integer points is always an integer, the distance itself isn't guaranteed to be an integer. Thus, we'll end up shoving a non-integer value into an integer variable, which truncates the decimal part. Using a floating point variable will probably work, but you should generally stay with integers whenever possible. + +</Spoiler> + +## Problems + +<Problems problems={problems.probs} /> diff --git a/content/2_Bronze/Intro_DS.mdx b/content/2_Bronze/Intro_DS.mdx new file mode 100644 index 0000000..d1b6ecd --- /dev/null +++ b/content/2_Bronze/Intro_DS.mdx @@ -0,0 +1,335 @@ +--- +id: intro-ds +title: Introduction to Data Structures +author: Darren Yao, Benjamin Qi +description: "Basic data structures in multiple languages, dynamic arrays and sorting." +frequency: 4 +--- + +import { Problem } from "../models"; + +export const problems = { + bubble: [ + new Problem("HR", "Bubble Sort", "https://www.hackerrank.com/challenges/ctci-bubble-sort/problem", "Very Easy", false, [], ""), + ], + easy: [ + new Problem("CSES", "Distinct Numbers", "1621", "Easy"), + new Problem("CF", "Kayaking", "contest/863/problem/B", "Easy", false, [], ""), + ], +}; + +<!-- + new Problem("Silver", "Teleportation", "812", "Very Hard", false, [], ""), + --> + +<Resources> + <Resource source="IUSACO" title="4.1 - Dynamic Arrays">module is based off this</Resource> + <Resource source="CPH" title="4.1 - Dynamic Arrays" starred>vectors, strings</Resource> + <Resource source="PAPS" title="3.1 - Vectors, 6.1 - Dynamic Arrays">how dynamic arrays work</Resource> + <Resource source="CPC" title="2 - Data Structures" url="02_data_structures">assumes prior experience</Resource> +</Resources> + +<br /> + +A **data structure** determines how data is stored (is it sorted? indexed? what operations does it support?). Each data structure supports some operations efficiently, while other operations are either inefficient or not supported at all. + +## Introduction + +<LanguageSection> + +<CPPSection> + +The C++ [standard library data structures](http://www.cplusplus.com/reference/stl/) are designed to store any type of data. We put the desired data type within the `<>` brackets when declaring the data structure, as follows: + +```cpp +vector<string> v; +``` + +This creates a `vector` structure that only stores objects of type `string`. + +For our examples below, we will primarily use the `int` data type, but note that you can use any data type including `string` and user-defined structures. + +Essentially every standard library data structure supports the `size()` method, which returns the number of elements in the data structure, and the `empty()` method, which returns `true` if the data structure is empty, and `false` otherwise. + +</CPPSection> + +<JavaSection> + +Java default [`Collections`](https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html) data structures are designed to store any type of object. However, we usually don't want this; instead, we want our data structures to only store one type of data, like integers, or strings. We do this by putting the desired data type within the `<>` brackets when declaring the data structure, as follows: + +```java +ArrayList<String> list = new ArrayList<String>(); +``` + +This creates an `ArrayList` structure that only stores objects of type `String`. + +For our examples below, we will primarily use the `Integer` data type, but note that you can have Collections of any object type, including `Strings` , other Collections, or user-defined objects. + +`Collections` data types always contain an `add` method for adding an element to the collection, and a `remove` method which removes and returns a certain element from the collection. They also support the `size()` method, which returns the number of elements in the data structure, and the `isEmpty()` method, which returns `true` if the data structure is empty, and `false` otherwise. + +</JavaSection> + +</LanguageSection> + +## Dynamic Arrays + +You're probably already familiar with regular (static) arrays. Now, there are also dynamic arrays ([`vector`](http://www.cplusplus.com/reference/vector/vector/) in C++, [`ArrayList`](https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html) in Java) that support all the functions that a normal array does, and can repeatedly reallocate storage to accommodate more elements as they are added. + +In a dynamic array, we can also add and delete elements at the end in $O(1)$ time. For example, the following code creates a dynamic array and adds the numbers $1$ through $10$ to it: + +<LanguageSection> + +<CPPSection> + +```cpp +vector<int> v; +for(int i = 1; i <= 10; i++){ + v.push_back(i); +} +``` + +</CPPSection> + +<JavaSection> + +```java +ArrayList<Integer> list = new ArrayList<Integer>(); +for(int i = 1; i <= 10; i++){ + list.add(i); +} +``` + +</JavaSection> + +</LanguageSection> + +In C++, we can give a dynamic array an initial size. The code below creates a dynamic array with $30$ zeroes. + +<LanguageSection> + +<CPPSection> + +```cpp +vector<int> v(30); // one way +vector<int> v; v.resize(30); // another way +``` + +</CPPSection> + +<JavaSection> + +Unfortunately, there is no Java equivalent; just use a for loop. + +```java +ArrayList<Integer> list = new ArrayList<Integer>(); +for (int i = 1; i <= 30; i++) list.add(0); +``` + +</JavaSection> + +</LanguageSection> + +We can ensure that a dynamic array's **capacity** must be at least $x$, meaning that it will not reallocate storage until we add more than $x$ elements to it. If the dynamic array's capacity is already at least $x$, then nothing occurs. Note that capacity is **not** the same thing as size. For example, if you declare a dynamic array with initial capacity $30$ and try to edit the element at index $5$, this will throw an error since the size of the dynamic array is still zero. + +The following code initializes a dynamic array with initial capacity $30$: + +<LanguageSection> + +<CPPSection> + +```cpp +vector<int> v; v.reserve(30); +``` + +</CPPSection> + +<JavaSection> + +```java +ArrayList<Integer> list = new ArrayList<Integer>(30); // one way +ArrayList<Integer> list = new ArrayList<Integer>(); list.ensureCapacity(30); // another way +``` + +</JavaSection> + +</LanguageSection> + +We can add and remove at any index of a dynamic array in $O(n)$ time. + +<LanguageSection> + +<CPPSection> + +```cpp +vector<int> v; +v.push_back(2); // [2] +v.push_back(3); // [2, 3] +v.push_back(7); // [2, 3, 7] +v.push_back(5); // [2, 3, 7, 5] +v[1] = 4; // sets element at index 1 to 4 -> [2, 4, 7, 5] +v.erase(v.begin() + 1); // removes element at index 1 -> [2, 7, 5] +// this remove method is O(n); to be avoided +v.push_back(8); // [2, 7, 5, 8] +v.erase(v.end()-1); // [2, 7, 5] +// here, we remove the element from the end of the list; this is O(1). +v.push_back(4); // [2, 7, 5, 4] +v.push_back(4); // [2, 7, 5, 4, 4] +v.push_back(9); // [2, 7, 5, 4, 4, 9] +cout << v[2]; // 5 +v.erase(v.begin(), v.begin()+3); // [4, 4, 9] +// this erases the first three elements; O(n) +``` + +</CPPSection> + +<JavaSection> + +```java +ArrayList<Integer> list = new ArrayList<Integer>(); +list.add(2); // [2] +list.add(3); // [2, 3] +list.add(7); // [2, 3, 7] +list.add(5); // [2, 3, 7, 5] +list.set(1, 4); // sets element at index 1 to 4 -> [2, 4, 7, 5] +list.remove(1); // removes element at index 1 -> [2, 7, 5] +// this remove method is O(n); to be avoided +list.add(8); // [2, 7, 5, 8] +list.remove(list.size()-1); // [2, 7, 5] +// here, we remove the element from the end of the list; this is $O(1)$. +System.out.println(list.get(2)); // 5 +``` + +</JavaSection> + +</LanguageSection> + +To iterate through a static or dynamic array, we can use either the regular for loop or the for-each loop. + +<LanguageSection> + +<CPPSection> + +```cpp +vector<int> v; +v.push_back(1); v.push_back(7); v.push_back(4); v.push_back(5); v.push_back(2); +int arr[] = {1, 7, 4, 5, 2}; +for(int i = 0; i < v.size(); i++){ + cout << v[i] << " "; +} +cout << endl; +for(int element : arr){ + cout << element << " "; +} +cout << endl; +``` + +</CPPSection> + +<JavaSection> + +```java +ArrayList<Integer> list = new ArrayList<Integer>(); +list.add(1); list.add(7); list.add(4); list.add(5); list.add(2); +int[] arr = {1, 7, 4, 5, 2}; +for(int i = 0; i < list.size(); i++){ // regular + System.out.println(list.get(i)); +} +for(int element : arr){ // for-each + System.out.println(element); +} +``` + +</JavaSection> + +</LanguageSection> + +In array-based contest problems, we'll use one-, two-, and three-dimensional static arrays most of the time. However, we can also have static arrays of dynamic arrays, dynamic arrays of static arrays, and so on. Usually, the choice between a static array and a dynamic array is just personal preference. + +## Iterators + +An **iterator** allows you to traverse a container by pointing to an object within the container. + +<LanguageSection> + +<CPPSection> + +However, they are **not** the same thing as pointers. For example, `vector.begin()` returns an iterator pointing to the first element of the vector. Apart from the standard way of traversing a vector (by treating it as an array), you can also use iterators: + +```cpp +for (vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) { + cout << *it; +} +``` + +C++11 and later versions can automatically infer the type of an object if you use the keyword `auto`, so the following are okay: + +```cpp +for (auto it = myvector.begin(); it != myvector.end(); ++it) { + cout << *it; +} +for(auto element : v) { + cout << element; +} +``` + +</CPPSection> + + +</LanguageSection> + + +## Sorting + +<Problems problems={problems.bubble} /> + +**Sorting** refers to arranging items in some particular order. + +<Resources> + <Resource source="CPH" title="3.1 - Sorting Theory"> </Resource> +</Resources> + +<!-- No bronze problem should require you to sort an array in $O(N\log N)$ time for Bronze, --> + +Although you do **not** need to know how sorting is implemented for Bronze, you should know how to use built-in methods to sort a (possibly dynamic) array. + +<LanguageSection> + +<CPPSection> + +<Resources> + <Resource source="CPH" title="3.2 - Sorting in C++" starred>can stop before comparison operators, which are covered in silver</Resource> + <Resource source="CPP" title="std::sort" url="https://en.cppreference.com/w/cpp/algorithm/sort">reference</Resource> + <Resource source="CF" title="C++ Tricks" url="blog/entry/74684">first two related to sorting</Resource> +</Resources> + +In order to sort a dynamic array, use `sort(v.begin(), v.end())` (or `sort(begin(v),end(v))`), whereas static arrays require `sort(arr, arr + N)` where $N$ is the number of elements to be sorted. The default sort function sorts the array in ascending order. + +</CPPSection> + +<JavaSection> + +In order to sort a static or dynamic array, use `Arrays.sort(arr)` or `Collections.sort(list)` respectively. The default sort function sorts the array in ascending order. + + - [Arrays.sort](https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#sort(java.lang.Object[])) + - [Collections.sort](https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#sort(java.util.List)) + +The `Arrays.sort()` function uses quicksort on primitive data types such as `long`s. This is fine for USACO, but in other contests such as CodeForces, it may time out on test cases specifically engineered to trigger worst-case $O(N^2)$ behavior in quicksort. See [here](https://codeforces.com/contest/1324/hacks/625031/) for an example of a solution that was hacked on CodeForces. + +Two ways to avoid this: + + - Declare the underlying array as an array of objects, for example `Long` instead of `long`. This forces the `Arrays.sort()` function to use mergesort, which is always $O(N \log N)$. + - [Shuffle](https://pastebin.com/k6gCRJDv) the array beforehand. + +</JavaSection> + +<PySection> + + - [Sorting Basics](https://docs.python.org/3/howto/sorting.html) + +</PySection> + +</LanguageSection> + +## Problems + +<Problems problems={problems.easy} /> diff --git a/content/2_Bronze/Intro_Graphs.mdx b/content/2_Bronze/Intro_Graphs.mdx new file mode 100644 index 0000000..23d2387 --- /dev/null +++ b/content/2_Bronze/Intro_Graphs.mdx @@ -0,0 +1,46 @@ +--- +id: intro-graphs +title: Introduction to Graphs +author: Darren Yao, Benjamin Qi +description: "Visualizing problems as graphs with resources covering basic terminology." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + general: [ + new Problem("Silver", "Grass Planting", "894", "Normal", false, ["tree"]), + new Problem("Bronze", "The Great Revegetation", "916", "Hard", false, []), + new Problem("Bronze", "Livestock Lineup", "965", "Hard", false, []), + new Problem("Bronze", "Milk Factory", "940", "Hard", false, ["tree"]), + new Problem("Bronze", "Family Tree", "833", "Hard", false, ["tree"]), + new Problem("Bronze", "Swapity Swap", "1013", "Hard", false, ["permutation"], "Hint: One option is to keep swapping until the permutation returns to its original state (but can you do better?)."), + new Problem("Bronze", "Cow Evolution", "941", "Hard", false, [], ""), + ] +}; + +Graphs can be used to represent many things, from images to wireless signals, but one of the simplest analogies is to a map. Consider a map with several cities and bidirectional roads connecting the cities. Some problems relating to graphs are: + + - Is city $A$ connected to city $B$? Consider a region to be a group of cities such that each city in the group can reach any other city in said group, but no other cities. How many regions are in this map, and which cities are in which region? + + - What's the shortest distance I have to travel to get from city $A$ to city $B$? + +Both of these tasks will be covered in higher divisions. For now, it suffices to learn how graphs are represented. + +<Resources> + <Resource source="CSA" title="Introduction to Graphs" url="introduction_to_graphs" starred>interactive</Resource> + <Resource source="CSA" title="Graph Representations" url="graph_representation" starred>interactive - adjacency lists and matrices</Resource> + <Resource source="CPH" title="11 - Basics of Graphs" starred>graph terminology, representation</Resource> + <Resource source="IUSACO" title="10.1 to 10.3 - Graph Theory" starred>graph basics and representation, trees</Resource> + <Resource source="PAPS" title="6.4 - Graphs">adjacency matrices, lists, maps</Resource> + <Resource source="TC" title="Intro to Graphs Section 1" url="introduction-to-graphs-and-their-data-structures-section-1"></Resource> +</Resources> + +## What Should You Know for Bronze? + +<IncompleteSection /> + +## Problems + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/2_Bronze/Intro_Greedy.mdx b/content/2_Bronze/Intro_Greedy.mdx new file mode 100644 index 0000000..28332e6 --- /dev/null +++ b/content/2_Bronze/Intro_Greedy.mdx @@ -0,0 +1,79 @@ +--- +id: intro-greedy +title: Introduction to Greedy Algorithms +author: Darren Yao +description: "Selecting the choice that seems to be the best at the moment at every step of your algorithm." +frequency: 2 +prerequisites: + - ad-hoc + - intro-ds +--- + +import { Problem } from "../models"; + +export const problems = { + tutorial: [ + new Problem("Bronze", "Mad Scientist", "1012", "Easy", false, [], ""), + ], + general: [ + new Problem("Bronze", "Cow Tipping", "689", "Normal", false, [], "Cells in the last row and column can be toggled uniquely. Toggle the appropriate ones and then recurse to the rectangle in the previous row/column, and solve the same way."), + new Problem("Bronze", "Out of Place", "785", "Hard", false, [], ""), + new Problem("Bronze", "Race", "989", "Very Hard", false, [], "Greedily increment/decrement Bessies speed to fit the conditions until her total distance exceeds K."), + ], +}; + +## Greedy Algorithms + +Some USACO Bronze problems that appear to be Ad Hoc can actually be solved using **greedy** algorithms. This idea will be covered in a future [module](../silver/greedy), but we'll introduce the general mindset in this section. + +<Resources> + <Resource source="CPH" title="6.1 - Coin Problem" starred>other examples are outside scope of bronze</Resource> +</Resources> + +From the above: + +> A **greedy** algorithm constructs a solution to the problem by always making a +choice that looks the best at the moment. A greedy algorithm never takes back +its choices, but directly constructs the final solution. For this reason, greedy +algorithms are usually very efficient. + +**Greedy** does not refer to a single algorithm, but rather a way of thinking that is applied to problems; there's no one way to do greedy algorithms. Hence, we use a selection of well-known examples to help you understand the greedy paradigm. + +<IncompleteSection /> + +### Example: Mad Scientist + +Try to come up with a greedy algorithm for the USACO Bronze problem "Mad Scientist." + +<Problems problems={problems.tutorial} /> + +<Spoiler title="Correct Greedy Algorithm"> + +In this problem, the correct greedy solution is to continually flip the longest possible ranges of mismatching cows. + +Mad Scientist has an excellent [editorial](http://www.usaco.org/current/data/sol_breedflip_bronze_feb20.html) with a video solution and intuitive proof. + +It is highly recommended you read it to gain a better understanding of the greedy algorithm. + +</Spoiler> + +However, not all greedy problems in the bronze division necessarily require mathematical proofs of correctness. It is often sufficent to intuitively convince yourself your algorithm is correct. + +<Info title="Pro Tip"> + +Sometimes, if the algorithm is easy enough to implement, you don't even need to convince yourself it's correct; just code it and see if it passes. Competitive programmers refer to this as "Proof by AC," or "Proof by Accepted." + +<!-- Don't overuse it though? --> + +</Info> + +## Problems + +<Problems problems={problems.general} /> + +<IncompleteSection> + +</IncompleteSection> + +<!-- Anything else major that needs to be added? Bronze Ad Hoc is not very complicated. --> + diff --git a/content/2_Bronze/Pairs_Tuples.mdx b/content/2_Bronze/Pairs_Tuples.mdx new file mode 100644 index 0000000..70ab3c3 --- /dev/null +++ b/content/2_Bronze/Pairs_Tuples.mdx @@ -0,0 +1,179 @@ +--- +id: pairs-tuples +title: Pairs & Tuples +author: Benjamin Qi, Nathan Wang, Darren Yao, Abutalib Namazov +description: "Storing two or more objects (possibly of different types) as a single unit." +prerequisites: + - intro-ds +--- + +Storing multiple elements as a single unit allows you to change the order of the elements (e.g. sorting) without mixing one component with another. + +**Example:** Given some points on a plane where the $i$-th point has coordinates $(x_i,y_i)$, sort these points in increasing order of $x$ coordinate and increasing order of $y$ in case there is a tie. + +If you sort one of $x_i$ and $y_i$ without considering the other, the points will not be in the correct order. We need to consider the pair $(x_i,y_i)$ as a single unit. + +<LanguageSection> +<CPPSection /> +<PySection> + +<Warning title="Language Note"> + +**Pairs are not available in Python.** Just use tuples; no need for pairs! + + +</Warning> + +<IncompleteSection /> +</PySection> +<JavaSection> + +<Warning title="Language Note"> + +**Pairs and tuples are not available in Java.** If you are using Java, just skip this module. + +</Warning> + +<IncompleteSection /> + +(add back class?) + +</JavaSection> +</LanguageSection> + +<LanguageSection> +<CPPSection> + +## [Pairs](http://www.cplusplus.com/reference/utility/pair/pair/) + + - `pair<type1, type2> p`: Creates a pair `p` with two elements with the first one being of `type1` and the second one being of `type2`. + - `make_pair(a, b)`: Returns a pair with values `a`, `b`. + - `{a, b}`: With C++11 and above, this can be used as to create a pair, which is easier to write than `make_pair(a, b)`. + - `pair.first`: The first value of the pair. + - `pair.second`: The second value of the pair. + +Example: + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + pair<string, int> myPair1 = make_pair("Testing", 123); + cout << myPair1.first << " " << myPair1.second << endl; // Testing 123 + myPair1.first = "It is possible to edit pairs after declaring them"; + cout << myPair1.first << " " << myPair1.second << endl; + + pair<string, string> myPair2 = {"Testing", "curly braces"}; + cout << myPair2.first << " " << myPair2.second << endl; // Testing curly braces +} + +/* Output + * Testing 123 + * Testing curly braces + * It is possible to edit pairs after declaring them 123 + */ +``` + + +## [Tuples](http://www.cplusplus.com/reference/tuple/) + +Of course, we can hold more than two values with something like `pair<int,pair<int,int>>`, but it gets messy when you need a lot of elements. In this case, using **tuples** might be more convenient. + + - `tuple<type1, type2, ..., typeN> t`: Creates a tuple with `N` elements, i'th one being of `typei`. + - `make_tuple(a, b, c, ..., d)`: Returns a tuple with values written in the brackets. + - `get<i>(t)`: Returns the $i$'th element of the tuple `t`. Only works for constant $i$. Can be used to change the element of a tuple. + +Note that it is illegal to do something like the following since $z$ is not constant: + +```cpp +tuple<int,int,int> t{3,4,5}; +int z = 1; cout << get<z>(t); +``` + + - `tie(a, b, c, ..., d) = t`: Equals `a, b, c, ..., d` to the elements of the tuple $t$ accordingly. + +This is not frequently used by competitive programmers, but it is good to know and can help simplify things sometimes. + +Example: + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + int a = 3, b = 4, c = 5; + tuple<int, int, int> t = tie(a, b, c); + cout << get<0>(t) << " " << get<1>(t) << " " << get<2>(t) << endl; + get<0>(t) = 7; + cout << get<0>(t) << " " << get<1>(t) << " " << get<2>(t) << endl; + + tuple<string, string, int> tp2 = make_tuple("Hello", "world", 100); + string s1, s2; int x; + tie(s1, s2, x) = tp2; + cout << s1 << " " << s2 << " " << x << endl; +} +/* Output + * 3 4 5 + * 7 4 5 + * Hello world 100 + */ +``` + +## Sorting Pairs & Tuples + +<Resources> + <Resource source="CPH" title="3.2 - Comparison Operators" starred></Resource> +</Resources> + +By default, C++ pairs are sorted by first element and then second element in case of a tie, which is precisely what we need for the example above. + +Example from CPH: + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + vector<pair<int,int>> v; + v.push_back({1,5}); + v.push_back({2,3}); + v.push_back({1,2}); + sort(v.begin(), v.end()); + for (pair<int,int> p: v) cout << p.first << " " << p.second << "\n"; +} + +/* Output: +1 2 +1 5 +2 3 +*/ +``` + +Tuples are sorted similarly. + +</CPPSection> + +<JavaSection> + +**Pairs and Tuples are not available in Java. Skip this module if you're a Java user!** + +</JavaSection> + +<PySection> + +## Python Tuples + +Use the link below (if you know of a better one, please let us know!) to learn about tuples. + +Python automatically sorts a list of tuples by the first element, then the second, and so on. This can save you time and keystrokes for certain problems like the one above. + +<Resources> + +<Resource source="Tutorialspoint" title="Python Tuples" url="https://www.tutorialspoint.com/python/python_tuples.htm" starred /> + +</Resources> + +</PySection> + +</LanguageSection>
\ No newline at end of file diff --git a/content/2_Bronze/Rect_Geo.mdx b/content/2_Bronze/Rect_Geo.mdx new file mode 100644 index 0000000..657804e --- /dev/null +++ b/content/2_Bronze/Rect_Geo.mdx @@ -0,0 +1,213 @@ +--- +id: rect-geo +title: "Rectangle Geometry" +author: Darren Yao, Michael Cao, Benjamin Qi +description: "Simple geometry problems in the Bronze division and library implementations to help you solve them easily." +frequency: 2 +--- +import { Problem } from "../models"; + +export const problems = { + blocked: [ + new Problem("Bronze", "Blocked Billboard", "759", "Easy", false, ["rect"]), + ], + general: [ + new Problem("Bronze", "Square Pasture", "663", "Very Easy", false, ["rect"]), + new Problem("Bronze", "Blocked Billboard II", "783", "Easy", false, ["rect"]), + new Problem("CF", "Div. 3 C - White Sheet", "contest/1216/problem/C", "Normal", false, ["rect"],"See this code (TODO; codeforces is down) for a nice implementation using the Java Rectangle class."), + ] +}; + +<Resources> + <Resource source="IUSACO" title="7.1 - Rectangle Geometry">module is based off this</Resource> +</Resources> + +<br/> + +Most only include two or three squares or rectangles, in which case you can simply draw out cases on paper. This should logically lead to a solution. + +## Example: Blocked Billboard + +<Problems problems={problems.blocked} /> + +### Naive Solution + +Since all coordinates are in the range $[-1000,1000]$, we can simply go through each of the $2000^2$ possible visible squares and check which ones are visible using nested for loops. + +<Spoiler title="Nested Loops"> + +<LanguageSection> + +<CPPSection> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +bool ok[2000][2000]; + +int main() { + freopen("billboard.in","r",stdin); + freopen("billboard.out","w",stdout); + for (int i = 0; i < 3; ++i) { + int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; + x1 += 1000, y1 += 1000, x2 += 1000, y2 += 1000; + for (int x = x1; x < x2; ++x) + for (int y = y1; y < y2; ++y) { + if (i < 2) ok[x][y] = 1; + else ok[x][y] = 0; + } + } + int ans = 0; + for (int x = 0; x < 2000; ++x) + for (int y = 0; y < 2000; ++y) + ans += ok[x][y]; + cout << ans << "\n"; +} +``` + +</CPPSection> + +</LanguageSection> + +</Spoiler> + +Of course, this wouldn't suffice if the coordinates were up to $10^9$. + +### Rectangle Class + +A useful class in Java for dealing with rectangle geometry problems (though definitely overkill) is the built-in [`Rectangle`](https://docs.oracle.com/javase/8/docs/api/java/awt/Rectangle.html) class. + +<LanguageSection> + +<JavaSection> + +To create a new rectangle, use the following constructor: + +```java +// creates a rectangle with upper-left corner at (x,y) with a specified width and height +Rectangle newRect = new Rectangle(x, y, width, height); +``` + +The `Rectangle` class supports numerous useful methods, including the following: + +- `firstRect.intersects(secondRect)` checks if two rectangles intersect. +- `firstRect.union(secondRect)` returns a rectangle representing the union of two rectangles. +- `firstRect.contains(x, y)` checks whether the integer point $(x,y)$ exists in firstRect. +- `firstRect.intersection(secondRect)` returns a rectangle representing the intersection of two rectangles. +- what happens when intersection is empty? + +This class can often lessen the implementation needed in a lot of bronze problems and CodeForces problems. + +For example, here is a nice implementation of the problem ([editorial](http://www.usaco.org/current/data/sol_billboard_bronze_dec17.html)). + +<Spoiler title="Java Solution With Built-in Rectangle Class"> + +```java +import java.awt.Rectangle; //needed to use Rectangle class +import java.io.*; +import java.util.*; + +public class blockedBillboard { + public static void main(String[] args) throws IOException{ + Scanner sc = new Scanner(new File("billboard.in")); + PrintWriter pw = new PrintWriter(new FileWriter("billboard.out")); + int x1, y1, x2, y2; + + // the top left point is (0,0), so you need to do -y2 + x1 = sc.nextInt(); y1 = sc.nextInt(); x2 = sc.nextInt(); y2 = sc.nextInt(); + Rectangle firstRect = new Rectangle(x1, -y2, x2-x1, y2-y1); + x1 = sc.nextInt(); y1 = sc.nextInt(); x2 = sc.nextInt(); y2 = sc.nextInt(); + Rectangle secondRect = new Rectangle(x1, -y2, x2-x1, y2-y1); + x1 = sc.nextInt(); y1 = sc.nextInt(); x2 = sc.nextInt(); y2 = sc.nextInt(); + Rectangle truck = new Rectangle(x1, -y2, x2-x1, y2-y1); + + long firstIntersect = getArea(firstRect.intersection(truck)); + long secondIntersect = getArea(secondRect.intersection(truck)); + pw.println(getArea(firstRect)+getArea(secondRect) + - firstIntersect - secondIntersect); + pw.close(); + } + public static long getArea(Rectangle r){ + if (r.isEmpty()) return 0; + return (long)r.getHeight()*(long)r.getWidth(); + } +} +``` +</Spoiler> + +<Spoiler title="Java Solution Without Built-in Rectangle Class"> + +```java +import java.io.*; +import java.util.*; + +class Rect { + int x1, y1, x2, y2; + Rect() {} + int area() { return (y2-y1)*(x2-x1); } +} + +public class blockedBillboard { + public static void main (String[] args) throws IOException{ + Scanner sc = new Scanner(new File("billboard.in")); + PrintWriter pw = new PrintWriter(new FileWriter("billboard.out")); + Rect a = new Rect(), b = new Rect(), t = new Rect(); + int x1, y1, x2, y2; + a.x1 = sc.nextInt(); a.y1 = sc.nextInt(); a.x2 = sc.nextInt(); a.y2 = sc.nextInt(); + b.x1 = sc.nextInt(); b.y1 = sc.nextInt(); b.x2 = sc.nextInt(); b.y2 = sc.nextInt(); + t.x1 = sc.nextInt(); t.y1 = sc.nextInt(); t.x2 = sc.nextInt(); t.y2 = sc.nextInt(); + pw.println(a.area()+b.area()-intersect(a, t)-intersect(b, t)); + pw.close(); + } + static int intersect(Rect p, Rect q) { + int xOverlap = Math.max(0, Math.min(p.x2, q.x2) - Math.max(p.x1, q.x1)); + int yOverlap = Math.max(0, Math.min(p.y2, q.y2) - Math.max(p.y1, q.y1)); + return xOverlap * yOverlap; + } +} +``` +</Spoiler> + +</JavaSection> + +<CPPSection> + +Unfortunately, C++ doesn't have a built in rectangle class, so you need to write the functions yourself. Here is the solution to Blocked Billboard written in C++ (thanks, Brian Dean!). A C++ [`struct`](http://www.cplusplus.com/doc/tutorial/structures/) is the same as a C++ `class` but all members are `public` rather than `private` by default. + +<Spoiler title="C++ Solution"> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Rect { + int x1,y1,x2,y2; + int area() { return (y2-y1)*(x2-x1); } +}; +int intersect(Rect p, Rect q){ + int xOverlap = max(0,min(p.x2,q.x2)-max(p.x1,q.x1)); + int yOverlap = max(0,min(p.y2,q.y2)-max(p.y1,q.y1)); + return xOverlap*yOverlap; +} + +int main(){ + ifstream cin ("billboard.in"); + ofstream cout ("billboard.out"); + Rect a, b, t; // billboards a, b, and the truck + cin >> a.x1 >> a.y1 >> a.x2 >> a.y2; + cin >> b.x1 >> b.y1 >> b.x2 >> b.y2; + cin >> t.x1 >> t.y1 >> t.x2 >> t.y2; + cout << a.area()+b.area()-intersect(a,t)-intersect(b,t) << endl; +} +``` +</Spoiler> + +</CPPSection> + +</LanguageSection> + + +## Problems + +<Problems problems={problems.general} /> diff --git a/content/3_Bronze/Simulation.mdx b/content/2_Bronze/Simulation.mdx index 06fac0a..fcb79d9 100644 --- a/content/3_Bronze/Simulation.mdx +++ b/content/2_Bronze/Simulation.mdx @@ -2,31 +2,37 @@ id: simulation title: "Simulation" author: Darren Yao -description: "In many problems, we can just simulate what we're told to do by the problem statement." +description: "Directly simulating the problem statement, which many Bronze problems allow you to do." frequency: 4 --- import { Problem } from "../models" -export const metadata = { - problems: { - bronze: [ - new Problem("Bronze", "Mixing Milk", "855", "Intro"), - new Problem("Bronze", "Speeding", "568", "Intro"), - new Problem("Bronze", "Angry Cows", "592", "Intro"), - new Problem("Bronze", "Milk Measurement", "761", "Intro"), - new Problem("Bronze", "Block Game", "664", "Intro"), - new Problem("Bronze", "Shell Game", "891", "Intro"), - new Problem("Bronze", "Team Tic Tac Toe", "831", "Intro"), - new Problem("Bronze", "The Lost Cow", "735", "Intro", false, [], ""), - new Problem("Bronze", "Why Did the Cow Cross the Road III", "713", "Intro", false, []), - new Problem("Bronze", "Mowing the Field", "593", "Intro", false, []), - new Problem("Bronze", "The Bovine Shuffle", "760", "Normal", false, []), - new Problem("Bronze", "Circular Barn", "616", "Normal", false, []), +export const problems = { + easier: [ + new Problem("Bronze", "Shell Game", "891", "Easy", false, ["Nested Loop"]), // 99.93 + new Problem("Bronze", "Mixing Milk", "855", "Easy", false, ["Single Loop"], "just pour 100 times"), // 99.87 + new Problem("Bronze", "Speeding", "568", "Easy", false, ["Nested Loop"], "store speed for each mile"), // 99.05 + new Problem("Bronze", "The Lost Cow", "735", "Easy", false, ["Single Loop"], ""), // 99.46 + new Problem("Bronze", "The Bovine Shuffle", "760", "Easy", false, []), // 100 + ], + harder: [ + new Problem("Bronze", "Circular Barn", "616", "Normal", false, []), // 99.25 + new Problem("Bronze", "Block Game", "664", "Normal"), // 97.41 + new Problem("Bronze", "Team Tic Tac Toe", "831", "Normal"), // 97.41 + new Problem("Bronze", "Cow Queue", "713", "Normal", false, []), // 97.59 + new Problem("Bronze", "Mowing the Field", "593", "Normal", false, []), // 97.16 + new Problem("Bronze", "Milk Measurement", "761", "Hard"), // 95.97 + new Problem("Bronze", "Angry Cows", "592", "Hard", false, [], ""), // 94.15 ] - } }; +<Resources> + <Resource source="IUSACO" title="5 - Simulation">module is based off this</Resource> +</Resources> + +<br /> + Since there's no formal algorithm involved, the intent of the problem is to assess competence with one's programming language of choice and knowledge of built-in data structures. At least in USACO Bronze, when a problem statement says to find the end result of some process, or to find when something occurs, it's usually sufficient to simulate the process naively. ## Example 1 @@ -45,13 +51,38 @@ The second line of the input contains $M$, $N$, $P$, and $Q$. Please output a single integer containing the number of seconds after the start at which Alice and Bob meet. If they never meet, please output $-1$. -### Solution +<Spoiler title="Solution"> -We can simulate the process. After inputting the values of $R$, $S$, $M$, $N$, $P$, and $Q$, we can keep track of Alice's and Bob's $x$- and $y$-coordinates. To start, we initialize variables for their respective positions. Alice's coordinates are initially \((0, 0)\), and Bob's coordinates are $(R, S)$ respectively. Every second, we increase Alice's $x$-coordinate by $M$ and her $y$-coordinate by $N$, and decrease Bob's $x$-coordinate by $P$ and his $y$-coordinate by $Q$. +We can simulate the process. After inputting the values of $R$, $S$, $M$, $N$, $P$, and $Q$, we can keep track of Alice's and Bob's $x$- and $y$-coordinates. To start, we initialize variables for their respective positions. Alice's coordinates are initially $(0, 0)$, and Bob's coordinates are $(R, S)$ respectively. Every second, we increase Alice's $x$-coordinate by $M$ and her $y$-coordinate by $N$, and decrease Bob's $x$-coordinate by $P$ and his $y$-coordinate by $Q$. Now, when do we stop? First, if Alice and Bob ever have the same coordinates, then we are done. Also, since Alice strictly moves up and to the right and Bob strictly moves down and to the left, if Alice's $x$- or $y$-coordinates are ever greater than Bob's, then it is impossible for them to meet. -Example Java code is displayed below. Here, as in other examples, input processing will be omitted). +Example code is displayed below. Here, as in other examples, input processing will be omitted. + +<LanguageSection> + +<CPPSection> + +```cpp +int ax = 0; int ay = 0; // alice's x and y coordinates +int bx = r; int by = s; // bob's x and y coordinates +int t = 0; // keep track of the current time +while(ax < bx && ay < by){ + // every second, update alice's and bob's coordinates and the time + ax += m; ay += n; + bx -= p; by -= q; + t++; +} +if(ax == bx && ay == by){ // if they are in the same location + cout << t << endl; // they meet at time t +} else { + cout << -1 << endl; // they never meet +} +``` + +</CPPSection> + +<JavaSection> ```java int ax = 0; int ay = 0; // alice's x and y coordinates @@ -64,21 +95,25 @@ while(ax < bx && ay < by){ t++; } if(ax == bx && ay == by){ // if they are in the same location - out.println(t); // they meet at time t + pw.println(t); // they meet at time t } else { - out.println(-1); // they never meet + pw.println(-1); // they never meet } -out.close(); // flush the output +pw.close(); // flush the output ``` -For C++, replaces lines such as `out.println(t)` with `cout << t << endl`. +</JavaSection> + +</LanguageSection> + +</Spoiler> ## Example 2 ### Statement -There are $N$ buckets ($5 \leq N \leq 10^5$), each with a certain capacity $C_i$ ($1 \leq C_i \leq 100$). One day, after a rainstorm, each bucket is filled with $A_i$ units of water ($1\leq A_i \leq C_i$). Charlie then performs the following process: he pours bucket 1 into bucket 2, then bucket 2 into bucket 3, and so on, up until pouring bucket $N-1$ into bucket $N$. When Charlie pours bucket $B$ into bucket $B+1$, he pours as much as possible until bucket $B$ is empty or bucket $B+1$ is full. Find out how much water is in each bucket once Charlie is done pouring. +There are $N$ buckets ($5 \leq N \leq 10^5$), each with a certain capacity $C_i$ ($1 \leq C_i \leq 100$). One day, after a rainstorm, each bucket is filled with $A_i$ units of water ($1\leq A_i \leq C_i$). Charlie then performs the following process: he pours bucket $1$ into bucket $2$, then bucket $2$ into bucket $3$, and so on, up until pouring bucket $N-1$ into bucket $N$. When Charlie pours bucket $B$ into bucket $B+1$, he pours as much as possible until bucket $B$ is empty or bucket $B+1$ is full. Find out how much water is in each bucket once Charlie is done pouring. #### Input Format @@ -92,15 +127,17 @@ The third line of the input contains the amount of water in each bucket $A_1, A_ Please print one line of output, containing $N$ space-separated integers: the final amount of water in each bucket once Charlie is done pouring. -### Solution +<Spoiler title="Solution"> -Once again, we can simulate the process of pouring one bucket into the next. The amount of milk poured from bucket $B$ to bucket $B+1$ is the smaller of the amount of water in bucket $B$ (after all previous operations have been completed) and the remaining space in bucket $B+1$, which is $C_{B+1} - A_{B+1}$. We can just handle all of these operations in order, using an array C to store the maximum capacities of each bucket, and an array A to store the current water level in each bucket, which we update during the process. Example code is below (note that arrays are zero-indexed, so the indices of our buckets go from $0$ to $N-1$ rather than from $1$ to $N$). +Once again, we can simulate the process of pouring one bucket into the next. The amount of milk poured from bucket $B$ to bucket $B+1$ is the smaller of the amount of water in bucket $B$ (after all previous operations have been completed) and the remaining space in bucket $B+1$, which is $C_{B+1} - A_{B+1}$. We can just handle all of these operations in order, using an array $C$ to store the maximum capacities of each bucket, and an array $A$ to store the current water level in each bucket, which we update during the process. Example code is below (note that arrays are zero-indexed, so the indices of our buckets go from $0$ to $N-1$ rather than from $1$ to $N$). -Java: +<LanguageSection> -```java +<CPPSection> + +```cpp for(int i = 0; i < n-1; i++){ - int amt = Math.min(A[i], C[i+1]-A[i+1]); + int amt = min(A[i], C[i+1]-A[i+1]); // the amount of water to be poured is the lesser of // the amount of water in the current bucket and // the amount of additional water that the next bucket can hold @@ -109,18 +146,19 @@ for(int i = 0; i < n-1; i++){ } for(int i = 0; i < n; i++){ - pw.print(A[i] + " "); + cout << A[i] << " "; // print the amount of water in each bucket at the end } -pw.println(); // print newline -pw.close(); // flush the output +cout << endl; ``` -C++: +</CPPSection> -```cpp +<JavaSection> + +```java for(int i = 0; i < n-1; i++){ - int amt = min(A[i], C[i+1]-A[i+1]); + int amt = Math.min(A[i], C[i+1]-A[i+1]); // the amount of water to be poured is the lesser of // the amount of water in the current bucket and // the amount of additional water that the next bucket can hold @@ -129,10 +167,25 @@ for(int i = 0; i < n-1; i++){ } for(int i = 0; i < n; i++){ - cout << A[i] << " "; + pw.print(A[i] + " "); // print the amount of water in each bucket at the end } -cout << endl; +pw.println(); // print newline +pw.close(); // flush the output ``` -<problems-list problems={metadata.problems.bronze} /> +</JavaSection> + +</LanguageSection> + +</Spoiler> + +## Problems + +### Easier + +<Problems problems={problems.easier} /> + +### Harder + +<Problems problems={problems.harder} /> diff --git a/content/2_Bronze/Time_Comp.mdx b/content/2_Bronze/Time_Comp.mdx new file mode 100644 index 0000000..af58746 --- /dev/null +++ b/content/2_Bronze/Time_Comp.mdx @@ -0,0 +1,382 @@ +--- +id: time-comp +title: "Time Complexity" +author: Darren Yao, Benjamin Qi +description: "Evaluating a program's time complexity, or how fast your program runs." +--- + +<Resources> + <Resource source="IUSACO" title="3 - Algorithm Analysis">module is based off this</Resource> + <Resource source="CPH" title="2 - Time Complexity" starred>Intro and examples</Resource> + <Resource source="PAPS" title="5 - Time Complexity">More in-depth. In particular, 5.2 gives a formal definition of Big O.</Resource> +</Resources> + +<br /> + +In programming contests, your program needs to finish running within a certain timeframe in order to receive credit. For USACO, this limit is $2$ seconds for C++ submissions, and $4$ seconds for Java/Python submissions. A conservative estimate for the number of <TextTooltip content={`You can think of an "operation" as one instruction for the computer. For example, multiplying two numbers together, reading in a number from the input, or outputting "Hello World" would all be considered "operations." `}>operations</TextTooltip> the grading server can handle per second is $10^8$, but it could be closer to $5 \cdot 10^8$ given good constant factors<Asterisk>If you don't know what constant factors are, don't worry -- we'll explain them below.</Asterisk>. + +## Complexity Calculations + +We want a method to calculate how many operations it takes to run each algorithm, in terms of the input size $n$. Fortunately, this can be done relatively easily using [Big O Notation](https://en.wikipedia.org/wiki/Big_O_notation), which expresses worst-case time complexity as a function of $n$ as $n$ gets arbitrarily large. Complexity is an upper bound for the number of steps an algorithm requires as a function of the input size. In Big O notation, we denote the complexity of a function as $O(f(n))$, where constant factors and lower-order terms are generally omitted from $f(n)$. We'll see some examples of how this works, as follows. + +The following code is $O(1)$, because it executes a constant number of operations. + +<LanguageSection> +<CPPSection> + +```cpp +int a = 5; +int b = 7; +int c = 4; +int d = a + b + c + 153; +``` + +</CPPSection> +<JavaSection> + +```java +int a = 5; +int b = 7; +int c = 4; +int d = a + b + c + 153; +``` + +</JavaSection> +<PySection> + +```py +a = 5 +b = 7 +c = 4 +d = a + b + c + 153 +``` + +</PySection> +</LanguageSection> + +Input and output operations are also assumed to be $O(1)$. In the following examples, we assume that the code inside the loops is $O(1)$. + +The time complexity of loops is the number of iterations that the loop runs. For example, the following code examples are both $O(n)$. + +<LanguageSection> +<CPPSection> + +```cpp +for (int i = 1; i <= n; i++) { + // constant time code here +} +``` + +```cpp +int i = 0; +while (i < n) { + // constant time node here + i++; +} +``` + +</CPPSection> +<JavaSection> + +```java +for (int i = 1; i <= n; i++) { + // constant time code here +} +``` + +```java +int i = 0; +while (i < n) { + // constant time node here + i++; +} +``` + +</JavaSection> +<PySection> + +```py +for i in range(1, n+1): + # constant time code here +``` + +```py +i = 0 +while (i < n): + # constant time node here + i += 1 +``` + +</PySection> +</LanguageSection> + +Because we ignore constant factors and lower order terms, the following examples are also $O(n)$: + + +<LanguageSection> +<CPPSection> + +```cpp +for (int i = 1; i <= 5*n + 17; i++) { + // constant time code here +} +``` + +```cpp +for (int i = 1; i <= n + 457737; i++) { + // constant time code here +} +``` + +</CPPSection> +<JavaSection> + +```java +for (int i = 1; i <= 5*n + 17; i++) { + // constant time code here +} +``` + +```java +for (int i = 1; i <= n + 457737; i++) { + // constant time code here +} +``` + +</JavaSection> +<PySection> + +```py +for i in range(5*n + 17): + # constant time code here + +for i in range(n + 457737): + # constant time code here +``` + +</PySection> +</LanguageSection> + + + +We can find the time complexity of multiple loops by multiplying together the time complexities of each loop. This example is $O(nm)$, because the outer loop runs $O(n)$ iterations and the inner loop $O(m)$. + +<LanguageSection> +<CPPSection> + +```cpp +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + // constant time code here + } +} +``` + +</CPPSection> +<JavaSection> + +```java +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + // constant time code here + } +} +``` + +</JavaSection> +<PySection> + + +```py +for i in range(n): + for j in range(m): + # constant time code here +``` + +</PySection> +</LanguageSection> + + + +In this example, the outer loop runs $O(n)$ iterations, and the inner loop runs anywhere between $1$ and $n$ iterations (which is a maximum of $n$). Since Big O notation calculates worst-case time complexity, we treat the inner loop as a factor of $n$.<Asterisk>We can also do some math to calculate exactly how many times the code runs: 1+2+...+n = n*(n-1)/2 = (n^2 - n)/2 = O(n^2)</Asterisk> Thus, this code is $O(n^2)$. + +<LanguageSection> +<CPPSection> + +```cpp +for (int i = 1; i <= n; i++) { + for (int j = i; j <= n; j++) { + // constant time code here + } +} +``` + +</CPPSection> +<JavaSection> + +```java +for (int i = 1; i <= n; i++) { + for (int j = i; j <= n; j++) { + // constant time code here + } +} +``` + +</JavaSection> +<PySection> + +```py +for i in range(n): + for j in range(i, n): + # constant time code here +``` + +</PySection> +</LanguageSection> + +If an algorithm contains multiple blocks, then its time complexity is the worst time complexity out of any block. For example, the following code is $O(n^2)$. + +<LanguageSection> +<CPPSection> + +```cpp +for (int i = 1; i <= n; i++) { + for(int j = 1; j <= n; j++) { + // constant time code here + } +} +for (int i = 1; i <= n + 58834; i++) { + // more constant time code here +} +``` + +</CPPSection> +<JavaSection> + +```java +for (int i = 1; i <= n; i++) { + for(int j = 1; j <= n; j++) { + // constant time code here + } +} +for (int i = 1; i <= n + 58834; i++) { + // more constant time code here +} +``` + +</JavaSection> +<PySection> + +```py +for i in range(n): + for j in range(n): + # constant time code here + +for i in range(n + 58834): + # more constant time code here +``` + +</PySection> +</LanguageSection> + +The following code is $O(n^2 + m)$, because it consists of two blocks of complexity $O(n^2)$ and $O(m)$, and neither of them is a lower order function with respect to the other. + +<LanguageSection> +<CPPSection> + +```cpp +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + // constant time code here + } +} +for (int i = 1; i <= m; i++) { + // more constant time code here +} +``` + +</CPPSection> +<JavaSection> + +```java +for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + // constant time code here + } +} +for (int i = i; j <= m; i++) { + // more constant time code here +} +``` + +</JavaSection> +<PySection> + +```py +for i in range(n): + for j in range(n): + # constant time code here + +for i in range(m): + # more constant time code here +``` + +</PySection> +</LanguageSection> + +## Constant Factor + +The **constant factor** of an algorithm refers to the coefficient of the complexity of an algorithm. If an algorithm runs in $O(kn)$ time, where $k$ is a constant and $n$ is the input size, then the "constant factor" would be $k$. + +<IncompleteSection> + +this is a terrible definition + +</IncompleteSection> + +Normally when using big-O notation, we ignore the constant factor: $O(3n) = O(n)$. This is fine most of the time, but sometimes we have an algorithm that just barely gets TLE, perhaps by just a few hundred milliseconds. When this happens, it is worth optimizing the constant factor of our algorithm. For example, if our code currently runs in $O(n^2)$ time, perhaps we can modify our code to make it run in $O(n^2/32)$ by using a bitset. (Of course, with big-O notation, $O(n^2) = O(n^2/32)$.) + +<IncompleteSection> + +fix math + +</IncompleteSection> + +For now, don't worry about how to optimize constant factors -- just be aware of them. + +## Common Complexities and Constraints + +Complexity factors that come from some common algorithms and data structures are as follows: + +- Mathematical formulas that just calculate an answer: $O(1)$ +- Unordered set/map: $O(1)$ per operation +- Binary search: $O(\log n)$ +- Ordered set/map or priority queue: $O(\log n)$ per operation +- Prime factorization of an integer, or checking primality or compositeness of an integer naively: $O(\sqrt{n})$ +- Reading in $n$ items of input: $O(n)$ +- Iterating through an array or a list of $n$ elements: $O(n)$ +- Sorting: usually $O(n \log n)$ for default sorting algorithms (mergesort, `Collections.sort`, `Arrays.sort`) +- Java Quicksort `Arrays.sort` function on primitives: $O(n^2)$ + - See "Introduction to Data Structures" for details. +- Iterating through all subsets of size $k$ of the input elements: $O(n^k)$. For example, iterating through all triplets is $O(n^3)$. +- Iterating through all subsets: $O(2^n)$ +- Iterating through all permutations: $O(n!)$ + + +Here are conservative upper bounds on the value of $n$ for each time complexity. You might get away with more than this, but this should allow you to quickly check whether an algorithm is viable. + +<center> + +| $n$ | Possible complexities | +| --------------------- | ----------------------------------- | +| $n \le 10$ | $O(n!)$, $O(n^7)$, $O(n^6)$ | +| $n \le 20$ | $O(2^n \cdot n)$, $O(n^5)$ | +| $n \le 80$ | $O(n^4)$ | +| $n \le 400$ | $O(n^3)$ | +| $n \le 7500$ | $O(n^2)$ | +| $n \le 7 \cdot 10^4$ | $O(n \sqrt n)$ | +| $n \le 5 \cdot 10^5$ | $O(n \log n)$ | +| $n \le 5 \cdot 10^6$ | $O(n)$ | +| $n \le 10^{18}$ | $O(\log^2 n)$, $O(\log n)$, $O(1)$ | + +</center> diff --git a/content/2_General/Contests.md b/content/2_General/Contests.md deleted file mode 100644 index 109684c..0000000 --- a/content/2_General/Contests.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -id: contests -title: Contests -author: Benjamin Qi -description: Contests that I participate in as well as a few tools. ---- - -See [clist.by](https://clist.by/coder/bqi343/) for an extensive list of contests. - -Make sure to [upsolve](https://en.wiktionary.org/wiki/upsolve) after the contest ends! - - - [AtCoder](https://beta.atcoder.jp/contests/archive) - - probably the highest quality, although difficulty isn't always reasonable - - Contests - - Beginner / Regular: 4 problems, 100 min - - Grand: 6 problems, ~110 min - - [Visualizer](https://kenkoooo.com/atcoder/#/table/Benq) - - I've done nearly all of the AGC problems. - - [Codeforces](http://codeforces.com/problemset) - - Contests - - Div 2, Div 1: 5 problems, 2 hrs - - Archive - - problem quality, difficulty ratings are ok but not always great - - [Topcoder](https://www.topcoder.com/my-dashboard/) - - Div 2, Div 1 - - 75 min coding, 15 min challenge - - Solutions only tested after contest! - - [Don Mills OJ](http://dmoj.ca/) - - at least one contest every month during school year - - [Google Kickstart](https://codingcompetitions.withgoogle.com/kickstart) - - Feb - Nov - - [Kattis](https://open.kattis.com/) - - misc ICPC contests - - [HackerEarth](http://hackerearth.com/) - - Monthly "Easy" - - quality is not always good - - [Codechef](http://codechef.com/) - - Lunchtime, Cookoff - - quality is not always good - -The following websites do not hold regular contests anymore, but they're still worth looking at. - - - [CS Academy](https://csacademy.com/contest/archive/) - - Contests - - Div 2: 5 problems, 2 hrs - - Open: 7 problems, 2 hrs - - no more? D: - - Archive - - short statements, editorials - - solve statistics - - ability to view best solutions - - I've done nearly all of the problems. :D - - [HackerRank](https://www.hackerrank.com/dashboard) - -## Annual Contests - - - [Google Code Jam](https://code.google.com/codejam/): Apr - - 25 from R3 - - [Facebook Hacker Cup](https://www.facebook.com/hackercup/): Jun-Jul -> Oct?? - - 25 from R3 - - [Topcoder Open](https://tco19.topcoder.com/): Apr-Aug -> Nov - - 4 from SRMs - - 10 from R4 - - 2 from Wildcard - - [AtCoder World Tour Finals](https://codeforces.com/blog/entry/56623) - - 8 from AGC - - [Bubble Cup](http://bubblecup.org/): Apr - -## US High School - - - [VT HSPC](https://icpc.cs.vt.edu/#/hscontest2017) - - Online - - 5 hours - - [Kattis](https://open.kattis.com/problem-sources/2016%20Virginia%20Tech%20High%20School%20Programming%20Contest) - - December - - [UCF HSPT](https://hspt.ucfprogrammingteam.org/index.php/hspt-online-edition) - - Online - - 4 hours - - December - - [Cornell HSPC](https://www.cs.cornell.edu/events/cornell-high-school-programming-contest) - - [2019](https://cornell-hspc19.kattis.com/problems) - - [PClassic (Philadelphia)](https://pclassic.org/) - - Java only :(( - - [mBIT (Montgomery Blair HS)](https://mbit.mbhs.edu/) - -## Codeforces Tools - - - [Stopstalk](https://www.stopstalk.com) - - [Code Drills](http://code-drills.com/) - - [CF Visualizer](http://cfviz.netlify.com/compare.html) - - [CF Rating Predictor](https://chrome.google.com/webstore/detail/cf-predictor/ocfloejijfhhkkdmheodbaanephbnfhn) - - [CF Command Line](https://codeforces.com/blog/entry/66552) - - [CF Editor](https://codeforces.com/blog/entry/72952) - - [CF Enhancer](https://chrome.google.com/webstore/detail/codeforces-enhancer/ocmandagmgmkcplckgnfgaokpgkfenmp) - - no longer works - -## Contest Tools - - - [2D Geo Visualizer](https://codeforces.com/blog/entry/70330) - - [CSAcademy Graph Editor (+ Geo Visualizer + Diff Tool)](https://csacademy.com/app/graph_editor/) - - [Desmos Grapher](https://www.desmos.com/calculator) - - [Wolfram Alpha](https://www.wolframalpha.com/) - - [OEIS](https://oeis.org/) diff --git a/content/2_General/Debugging.md b/content/2_General/Debugging.md deleted file mode 100644 index 467047e..0000000 --- a/content/2_General/Debugging.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -id: debugging -title: Debugging -author: Benjamin Qi, Aaron Chew -description: Detecting issues within your program and figuring out how to avoid them in the first place. ---- - -## General - -(asserts?) - -### Using a Debugger - -Using a debugger varies from language to language and even IDE to different IDE. For now I will describe the basic operations of a debugger. - -A debugger allows you to pause a code in its execution and see the values as a given point in the debugger. - -To do this, set a "breakpoint" at a certain line of code. When the code runs to that breakpoint, it will pause and you will be able to inspect all the different variables at that certain instance. - -There are two more useful and common operations. Once you are at the breakpoint, you may want to see what happens after the current line is executed. This would be the "Step Over" button that will allow you to move to the next line. Say you are at a line with the following code: `dfs(0,-1)`, if you click "step over" the debugger will ignore showing you what happens in this function and go to the next line. If you click "step in," however, you will enter the function and be able to step through that function. - -In essense, a debugger is a tool to "trace code" for you. It is not much different from just printing the values out at various points in your program. - -Pros of using a debugger: - - - No need to write print statements so you save time - - You can step through the code in real time - -Cons of using a debugger: - - - You cannot see the overall "output" of your program at each stage. For example, if I wanted to see every single value of `i` in the program, I could not using a debugger. - - Most advanced competitive programmers do not use debuggers; it is quite time inefficient. - -(actual details?) - -## C++ - -### Style Guide - -[Competitive C++ Manifesto: A Style Guide](https://codeforces.com/blog/entry/64218) - -### Compilation - -I use the following to compile and run. - -``` -co() { g++ -std=c++17 -O2 -o $1 $1.cpp -Wall -Wextra -Wshadow -DLOCAL -Wl,-stack_size -Wl,0xF0000000; } -run() { co $1 && ./$1 & fg; } -``` - -#### Warnings - -See [here](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html). - -`-Wall -Wextra -Wshadow` - -[Variable shadowing](https://en.wikipedia.org/wiki/Variable_shadowing) should be avoided whenever possible. - -#### Stack Size - -According to [this comment](https://codeforces.com/blog/entry/60999?#comment-449312), `-Wl,-stack_size -Wl,0xF0000000` increases the stack size on Mac (otherwise, you might get a runtime error). - -(Documentation?) - -#### Other - -`-fsanitize=undefined -fsanitize=address` - -`-D_GLIBCXX_DEBUG -g` - -(someone want to explain these? I don't use) - -### Running - -According to [StackOverflow](https://stackoverflow.com/a/60516966/5834770) the `& fg` is necessary for getting `zsh` on Mac to display crash messages (such as segmentation fault). - -#### Measuring Time & Memory Usage - - - [CF Comment](https://codeforces.com/blog/entry/49371?#comment-333749) - - [time -v on Mac](https://stackoverflow.com/questions/32515381/mac-os-x-usr-bin-time-verbose-flag) - -### CppIO Template - -Although not feasible if you have to write all code from scratch, I find [this template](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/CppIO.h) very helpful for simplifying input / output / debug output. Note that `dbg()` only produces debug output when `-DLOCAL` is included as part of the compilation command, so you don't need to comment out those lines before submitting. - -[Examples - Debug Output](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/CppIO_test.cpp) - -### Stress Testing - -You can use a [simple script](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/stress.sh) to test two solutions against each other. See Errichto's [video](https://www.youtube.com/watch?v=JXTVOyQpSGM) on testing solutions for more information. diff --git a/content/2_General/Macros.md b/content/2_General/Macros.md deleted file mode 100644 index 03ffcaa..0000000 --- a/content/2_General/Macros.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -id: macros -title: C++ Macros -author: Benjamin Qi -description: Shortening C++ code and making it (un?)readable. ---- - -## Introduction - - - [GeeksForGeeks](https://www.geeksforgeeks.org/cc-preprocessors/#:~:text=Macros%3A%20Macros%20are%20a%20piece,used%20to%20define%20a%20macro.) - - [GCC Online Docs](https://gcc.gnu.org/onlinedocs/cpp/Macros.html) - - CPH 1.4 - -## [My Template](https://github.com/bqi343/USACO/blob/master/Implementations/content/contest/template.cpp) - -Some comments about specific parts: - -### Pairs - -```cpp -typedef pair<int,int> pi; - -#define mp make_pair -#define f first -#define s second -``` - -Pretty annoying to keep typing `first` and `second` (especially if you have nested pairs ...) - -### Vectors - -```cpp -typedef vector<int> vi; - -#define sz(x) (int)x.size() -#define all(x) begin(x), end(x) -``` - -It's generally a good idea to convert a size to a signed integer before doing anything with it to avoid cases like the following. - -```cpp -vi x; -cout << x.size()-1 << "\n"; // 18446744073709551615 -cout << sz(x)-1 << "\n"; // -1 -``` - -`all(v)` makes sorting part or all of a vector a bit shorter. - -```cpp -vi v = {2,4,1,5,3}; -sort(1+all(v)); // {2,1,3,4,5} -sort(all(v)); // {1,2,3,4,5} -``` - -### Constants - -```cpp -const int MOD = 1e9+7; // 998244353; -const int MX = 2e5+5; -const ll INF = 1e18; -const ld PI = acos((ld)-1); -``` - -$10^9+7$ is a prime that appears quite frequently in programming contests. Interestingly, $(10^9+7-1)/2=5\cdot 10^8+3$ is also prime. On the other hand, $998244353-1$ is divisible by $2^{23}$, which is useful for [NTT](https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general)#Number-theoretic_transform). - -Most USACO problems satisfy $N\le 2\cdot 10^5$. - -### RNG - -```cpp -mt19937 rng((uint32_t)chrono::steady_clock::now().time_since_epoch().count()); -``` - -See [neal's blog](https://codeforces.com/blog/entry/61587) about why `rand()` is bad. Use `rng()` instead. - -### ckmin - -```cpp -template<class T> bool ckmin(T& a, const T& b) { - return b < a ? a = b, 1 : 0; } -template<class T> bool ckmax(T& a, const T& b) { - return a < b ? a = b, 1 : 0; } -``` - -See [negCyc](https://github.com/bqi343/USACO/blob/master/Implementations/content/graphs%20(12)/Basics/NegativeCycle%20(7.3).h) for an example of usage.
\ No newline at end of file diff --git a/content/2_General/Why_Cpp.md b/content/2_General/Why_Cpp.md deleted file mode 100644 index 85440a9..0000000 --- a/content/2_General/Why_Cpp.md +++ /dev/null @@ -1,227 +0,0 @@ ---- -id: why-cpp -title: Why C++? -author: Benjamin Qi -description: Some reasons why choice of language matters significantly outside of USACO Bronze. ---- - -## Time Limit - -Although both Python and Java receive two times the C++ time limit in USACO, this is not the case for most other websites (ex. CodeForces). Even with the extended time limits, Python and Java sometimes have trouble passing. - - - Rewriting the C++ solution for [USACO Silver Wormsort](http://www.usaco.org/index.php?page=viewproblem2&cpid=992) in Python receives TLE (Time Limit Exceeded) on 2/10 cases. I'm not sure whether it is possible to pass this problem with Python. - - <spoiler title="Python3 8/10 Solution"> - - ```py - # 8/10 test cases ... - - fin = open("wormsort.in","r") - lines = [line for line in fin] - N,M = map(int,lines[0].split()) - p = list(map(lambda x: int(x)-1,lines[1].split())) - - ed = [] - for i in range(2,len(lines)): - a,b,w = map(int,lines[i].split()) - a -= 1 - b -= 1 - ed.append([w,a,b]) - ed.sort() - ed.reverse() - - adj = [[] for i in range(N)] - vis = [0 for i in range(N)] - cnt = 0 - - def dfs(x): - global cnt - if vis[x] != 0: - return - vis[x] = cnt - for i in adj[x]: - dfs(i) - - def ok(mid): - global cnt - for i in range(N): - vis[i] = 0 - adj[i].clear() - for i in range(mid): - a,b = ed[i][1],ed[i][2] - adj[a].append(b) - adj[b].append(a) - for i in range(N): - if vis[i] == 0: - cnt += 1 - todo = [i] - ind = 0 - while ind < len(todo): - x = todo[ind] - ind += 1 - vis[x] = cnt - for i in adj[x]: - if vis[i] == 0: - vis[i] = -cnt - todo.append(i) - ok = True - for i in range(N): - if vis[i] != vis[p[i]]: - ok = False - return ok - - lo,hi = 0,M - while lo < hi: - mid = (lo+hi)//2 - if ok(mid): - hi = mid - else: - lo = mid+1 - - fout = open("wormsort.out","w") - - fout.write(str(-1 if lo == 0 else ed[lo-1][0])) - fout.write('\n') - ``` - - </spoiler> - - - A similar solution in Java requires almost 3s, which is fairly close to the time limit of 4s. - - <spoiler title="Java Solution"> - - ```java - import java.io.*; // from Nick Wu - import java.util.*; - public class wormsort { - public static void main(String[] args) throws IOException{ - BufferedReader br = new BufferedReader(new FileReader("wormsort.in")); - StringTokenizer st = new StringTokenizer(br.readLine()); - int n = Integer.parseInt(st.nextToken()); - int m = Integer.parseInt(st.nextToken()); - loc = new int[n]; - component = new int[n]; - edges = new LinkedList[n]; - for(int i = 0; i < n; i++) edges[i] = new LinkedList<>(); - lhs = new int[m]; - rhs = new int[m]; - weight = new int[m]; - st = new StringTokenizer(br.readLine()); - for(int i = 0; i < n; i++) loc[i] = Integer.parseInt(st.nextToken())-1; - for(int i = 0; i < m; i++) { - st = new StringTokenizer(br.readLine()); - lhs[i] = Integer.parseInt(st.nextToken())-1; - rhs[i] = Integer.parseInt(st.nextToken())-1; - weight[i] = Integer.parseInt(st.nextToken()); - } - br.close(); - int minW = 0; - int maxW = 1000000001; - while(minW != maxW) { - int mid = (minW + maxW + 1) / 2; - if(valid(mid)) minW = mid; - else maxW = mid-1; - } - if(minW > 1e9) minW = -1; - PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("wormsort.out"))); - pw.println(minW); - pw.close(); - } - static int[] loc, lhs, rhs, weight; - static LinkedList<Integer>[] edges; - static int[] component; - private static void dfs(int curr, int label) { - if(component[curr] == label) return; - component[curr] = label; - for(int child: edges[curr]) dfs(child, label); - } - private static boolean valid(int minW) { - Arrays.fill(component, -1); - for(int i = 0; i < edges.length; i++) edges[i].clear(); - for(int i = 0; i < lhs.length; i++) { - if(weight[i] >= minW) { - edges[lhs[i]].add(rhs[i]); - edges[rhs[i]].add(lhs[i]); - } - } - int numcomps = 0; - for(int i = 0; i < component.length; i++) { - if(component[i] < 0) { - dfs(i, numcomps++); - } - } - for(int i = 0; i < loc.length; i++) { - if(component[i] != component[loc[i]]) return false; - } - return true; - } - } - ``` - - </spoiler> - - - A comparable C++ solution runs in less than 700ms. - - <spoiler title="C++ Solution"> - - ```cpp - #include <bits/stdc++.h> - using namespace std; - - typedef vector<int> vi; - - const int MX = 1e5+5; - - int loc[MX], comp[MX], lhs[MX], rhs[MX], wei[MX]; - vi adj[MX]; - int n,m; - - void dfs(int cur, int label) { - if (comp[cur] == label) return; - comp[cur] = label; - for (int c: adj[cur]) dfs(c,label); - } - - bool valid(int minW) { - for (int i = 0; i < n; ++i) { - comp[i] = -1; - adj[i].clear(); - } - for (int i = 0; i < m; ++i) if (wei[i] >= minW) - adj[lhs[i]].push_back(rhs[i]), adj[rhs[i]].push_back(lhs[i]); - int numComps = 0; - for (int i = 0; i < n; ++i) if (comp[i] < 0) - dfs(i,numComps++); - for (int i = 0; i < n; ++i) - if (comp[i] != comp[loc[i]]) return 0; - return 1; - } - - int main() { - ios_base::sync_with_stdio(0); cin.tie(0); - freopen("wormsort.in","r",stdin); - freopen("wormsort.out","w",stdout); - cin >> n >> m; - for (int i = 0; i < n; ++i) cin >> loc[i], loc[i] --; - for (int i = 0; i < m; ++i) { - cin >> lhs[i], lhs[i] --; - cin >> rhs[i], rhs[i] --; - cin >> wei[i]; - } - int minW = 0, maxW = (int)1e9+1; - while (minW != maxW) { - int mid = (minW+maxW+1)/2; - if (valid(mid)) minW = mid; - else maxW = mid-1; - } - if (minW > 1e9) minW = -1; - cout << minW; - } - ``` - </spoiler> - -## Other Notes - - - USACO problemsetters don't always test Java (and rarely Python) solutions when setting constraints. - - Python lacks a data structure that keeps its keys in sorted order (the equivalent of `set` in C++), which is required for some silver problems. - - Java lacks features such as `#define`, `typedef`, and `auto` that are present in C++ (which some contestants rely on extensively, see "macros"). diff --git a/content/3_Bronze/Complete_Search.mdx b/content/3_Bronze/Complete_Search.mdx deleted file mode 100644 index 631cb61..0000000 --- a/content/3_Bronze/Complete_Search.mdx +++ /dev/null @@ -1,160 +0,0 @@ ---- -id: complete-search -title: "Complete Search" -author: Darren Yao -description: In many problems (especially in Bronze) it suffices to check all possible cases in the solution space. -frequency: 4 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - cses: [ - new Problem("CSES", "Creating Strings I", "1622", "Intro", false, []), - new Problem("CSES", "Apple Division", "1623", "Intro", false, []), - new Problem("CSES", "Chessboard and Queens", "1624", "Easy", false, []), - ], - bronze: [ - new Problem("Bronze", "Milk Pails", "615", "Intro", false, [], "Hint: Fix the number of first-type operations you perform."), - new Problem("Bronze", "Triangles", "1011", "Easy", false, [], ""), - new Problem("Bronze", "Cow Gymnastics", "963", "Easy", false, [], "Hint: Brute force over all possible pairs."), - new Problem("Bronze", "Lifeguards", "784", "Easy", false, [], "Hint: Try removing each lifeguard one at a time."), - new Problem("Bronze", "Where Am I?", "964", "Easy", false, [], "Hint: Brute force over all possible substrings."), - new Problem("Bronze", "Photoshoot", "988", "Normal", false, [], "Hint: Figure out what exactly you're complete searching."), - new Problem("Bronze", "Field Reduction", "641", "Normal", false, [], "Hint: For this problem, you can't do a full complete search; you have to do a reduced search."), - new Problem("Bronze", "Back & Forth", "857", "Hard", false, [], ""), - new Problem("Bronze", "Livestock Lineup", "965", "Hard", false, ["permutations"], ""), - ], - silver: [ - new Problem("Silver", "Bovine Genomics", "739", "Normal", false, [], ""), - new Problem("Silver", "Field Reduction", "642", "Normal", false, [], ""), - ] - } -}; - - -## Additional Reading - - - CPH 5.1 - 5.3 (Complete Search) - -# Complete Search - -In many problems (especially in Bronze) it suffices to check all possible cases in the solution space, whether it be all elements, all pairs of elements, or all subsets, or all permutations. Unsurprisingly, this is called **complete search** (or **brute force**), because it completely searches the entire solution space. - -## Example 1 - -### Statement - -You are given $N$ $(3 \leq N \leq 5000)$ integer points on the coordinate plane. Find the square of the maximum Euclidean distance (aka length of the straight line) between any two of the points. - -#### Input Format - -The first line contains an integer $N$. - -The second line contains $N$ integers, the $x$-coordinates of the points: $x_1, x_2, \dots, x_N$ ($-1000 \leq x_i \leq 1000$). - -The third line contains $N$ integers, the $y$-coordinates of the points: $y_1, y_2, \dots, y_N$ ($-1000 \leq y_i \leq 1000$). - -#### Output Format - -Print one integer, the square of the maximum Euclidean distance between any two of the points. - -### Solution - -We can brute-force every pair of points and find the square of the distance between them, by squaring the formula for Euclidean distance: $\text{distance}^2 = (x_2-x_1)^2 + (y_2-y_1)^2$. Thus, we store the coordinates in arrays `X[]` and `Y[]`, such that `X[i]` and `Y[i]` are the $x$- and $y$-coordinates of the $i_{th}$ point, respectively. Then, we iterate through all possible pairs of points, using a variable max to store the maximum square of distance between any pair seen so far, and if the square of the distance between a pair is greater than our current maximum, we set our current maximum to it. - -Java: - -```java -int max = 0; // storing the current maximum -for(int i = 0; i < n; i++){ // for each first point - for(int j = i+1; j < n; j++){ // for each second point - int dx = x[i] - x[j]; - int dy = y[i] - y[j]; - max = Math.max(max, dx*dx + dy*dy); - // if the square of the distance between the two points is greater than - // our current maximum, then update the maximum - } -} -pw.println(max); -``` - -C++ - -```cpp -int high = 0; // storing the current maximum -for(int i = 0; i < n; i++){ // for each first point - for(int j = i+1; j < n; j++){ // for each second point - int dx = x[i] - x[j]; - int dy = y[i] - y[j]; - high = max(high, dx*dx + dy*dy); - // if the square of the distance between the two points is greater than - // our current maximum, then update the maximum - } -} -cout << high << endl; -``` - -A couple notes: - - - First, since we're iterating through all pairs of points, we start the $j$ loop from $j = i+1$ so that point $i$ and point $j$ are never the same point. Furthermore, it makes it so that each pair is only counted once. In this problem, it doesn't matter whether we double-count pairs or whether we allow $i$ and $j$ to be the same point, but in other problems where we're counting something rather than looking at the maximum, it's important to be careful that we don't overcount. - - Secondly, the problem asks for the square of the maximum Euclidean distance between any two points. Some students may be tempted to maintain the maximum distance in a variable, and then square it at the end when outputting. However, the problem here is that while the square of the distance between two integer points is always an integer, the distance itself isn't guaranteed to be an integer. Thus, we'll end up shoving a non-integer value into an integer variable, which truncates the decimal part. Using a floating point variable isn't likely to work either, due to precision errors (use of floating point decimals should generally be avoided when possible). - -(Ben - have you verified this claim?) - -## Generating Permutations - -A **permutation** is a reordering of a list of elements. Some problems will ask for an ordering of elements that satisfies certain conditions. In these problems, if $N \leq 10$, we can probably iterate through all permutations and check each permutation for validity. For a list of $N$ elements, there are $N!$ ways to permute them, and generally we'll need to read through each permutation once to check its validity, for a time complexity of $O(N \cdot N!)$. - -In Java, we'll have to implement this ourselves, which is called [Heap's Algorithm](https://en.wikipedia.org/wiki/Heap%27s_algorithm) (no relation to the heap data structure). What's going to be in the check function depends on the problem, but it should verify whether the current permutation satisfies the constraints given in the problem. - -As an example, here are the permutations generated by Heap's Algorithm for \([1, 2, 3]\): - -$$ -[1, 2, 3], [2, 1, 3], [3, 1, 2], [1, 3, 2], [2, 3, 1], [3, 2, 1] -$$ - -Code for iterating over all permutations is as follows: - -```java -// this method is called with k equal to the length of arr -static void generate(int[] arr, int k){ - if(k == 1){ - check(arr); // check the current permutation for validity - } else { - generate(arr, k-1); - for(int i = 0; i < k-1; i++){ - if(k % 2 == 0){ - swap(arr, i, k-1); - // swap indices i and k-1 of arr - } else { - swap(arr, 0, k-1); - // swap indices 0 and k-1 of arr - } - } - } -} -``` -In C++, we can just use the `next_permutation()` function. This function takes in a range and modifies it to the next greater permutation. If there is no greater permutation, it returns false. To iterate through all permutations, place it inside a `do-while` loop. We are using a `do-while` loop here instead of a typical `while` loop because a `while` loop would modify the smallest permutation before we got a chance to process it. -```cpp -do { - check(v); // process or check the current permutation for validity -} while(next_permutation(v.begin(), v.end())); -``` - -Note that this generates the permutations in **lexicographical order** (explanation) - -## Problems - -<problems-list problems={metadata.problems.cses} /> - -<problems-list problems={metadata.problems.bronze} /> - -Load Balancing: 617 -Bull in a China Shop: 640 -Bovine Genomics: 736 -Diamond Collector: 639 -Circle Cross: 712 - -<problems-list problems={metadata.problems.silver} />
\ No newline at end of file diff --git a/content/3_Bronze/Cpp_Containers.mdx b/content/3_Bronze/Cpp_Containers.mdx deleted file mode 100644 index b4db914..0000000 --- a/content/3_Bronze/Cpp_Containers.mdx +++ /dev/null @@ -1,222 +0,0 @@ ---- -id: containers -title: Built-In C++ Containers -author: Darren Yao -description: Introduces C++ containers that are frequently used in competitive programming. -frequency: 4 ---- - -## Additional Resources - - - CPH 4 (Data Structures) - - [PAPS 3.1, 3.5, 6.1](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [CPC.2](https://github.com/SuprDewd/T-414-AFLV/tree/master/02_data_structures) - -# Data Structures - -A **data structure** determines how data is stored (is it sorted? indexed? what operations does it support?). Each data structure supports some operations efficiently, while other operations are either inefficient or not supported at all. - -The C++ [standard library data structures](http://www.cplusplus.com/reference/stl/) are designed to store any type of data. We put the desired data type within the `<>` brackets when declaring the data structure, as follows: - -```cpp -vector<string> v; -``` - -This creates a `vector` structure that only stores objects of type `string`. - -For our examples below, we will primarily use the `int` data type, but note that you can use any data type including `string` and user-defined structures. - -Essentially every standard library data structure supports the `size()` method, which returns the number of elements in the data structure, and the `empty()` method, which returns `true` if the data structure is empty, and `false` otherwise. - -## Dynamic Arrays - -You're probably already familiar with regular (static) arrays. Now, there are also dynamic arrays (`vector` in C++) that support all the functions that a normal array does, and can resize itself to accommodate more elements. In a dynamic array, we can also add and delete elements at the end in $O(1)$ time. - -For example, the following code creates a dynamic array and adds the numbers $1$ through $10$ to it: - -```cpp -vector<int> v; -for(int i = 1; i <= 10; i++){ - v.push_back(i); -} -``` - -When declaring a dynamic array we can give it an initial size, so it doesn't resize itself as we add elements to it. The following code initializes a `vector` with initial size $30$: - -```cpp -vector<int> v(30); -``` - -However, we need to be careful that we only add elements to the end of the `vector`; insertion and deletion in the middle of the `vector` is $O(n)$. - -```cpp -vector<int> v; -v.push_back(2); // [2] -v.push_back(3); // [2, 3] -v.push_back(7); // [2, 3, 7] -v.push_back(5); // [2, 3, 7, 5] -v[1] = 4; // sets element at index 1 to 4 -> [2, 4, 7, 5] -v.erase(v.begin() + 1); // removes element at index 1 -> [2, 7, 5] -// this remove method is O(n); to be avoided -v.push_back(8); // [2, 7, 5, 8] -v.erase(v.end()-1); // [2, 7, 5] -// here, we remove the element from the end of the list; this is O(1). -v.push_back(4); // [2, 7, 5, 4] -v.push_back(4); // [2, 7, 5, 4, 4] -v.push_back(9); // [2, 7, 5, 4, 4, 9] -cout << v[2]; // 5 -v.erase(v.begin(), v.begin()+3); // [4, 4, 9] -// this erases the first three elements; O(n) -``` - -To iterate through a static or dynamic array, we can use either the regular for loop or the for-each loop. - -```cpp -vector<int> v; -v.push_back(1); v.push_back(7); v.push_back(4); v.push_back(5); v.push_back(2); -int arr[] = {1, 7, 4, 5, 2}; -for(int i = 0; i < v.size(); i++){ - cout << v[i] << " "; -} -cout << endl; -for(int element : arr){ - cout << element << " "; -} -cout << endl; -``` - -In order to sort a dynamic array, use `sort(v.begin(), v.end())` (or `sort(begin(v),end(v))`), whereas static arrays require `sort(arr, arr + N)` where $N$ is the number of elements to be sorted. The default sort function sorts the array in ascending order. - -In array-based contest problems, we'll use one-, two-, and three-dimensional static arrays most of the time. However, we can also have static arrays of dynamic arrays, dynamic arrays of static arrays, and so on. Usually, the choice between a static array and a dynamic array is just personal preference. - -## Iterators - -An **iterator** allows you to traverse a container by pointing to an object within the container (although they are **not** the same thing as pointers). For example, `vector.begin()` returns an iterator pointing to the first element of the vector. Apart from the standard way of traversing a vector (by treating it as an array), you can also use iterators: - -```cpp -for (vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it) { - cout << *it; //prints the values in the vector using the pointer -} -``` - -C++11 and later versions can automatically infer the type of an object if you use the keyword `auto`. This means that you can replace `vector<int>::iterator` with `auto` or `int` with `auto` in the for-each loop. - -```cpp -for(auto element : v) { - cout << element; //prints the values in the vector -} -``` - -## Sets and Maps - -A set is a collection of objects that contains no duplicates. There are two types of sets: unordered sets (`unordered_set` in C++), and ordered set (`set` in C++). - -### Unordered Sets - -The unordered set works by hashing, which is assigning a unique code to every variable/object which allows insertions, deletions, and searches in $O(1)$ time, albeit with a high constant factor, as hashing requires a large constant number of operations. However, as the name implies, elements are not ordered in any meaningful way, so traversals of an unordered set will return elements in some arbitrary order. The operations on an unordered set are `insert`, which adds an element to the set if not already present, `erase`, which deletes an element if it exists, and `count`, which returns 1 if the set contains the element and 0 if it doesn't. - -```cpp -unordered_set<int> s; -s.insert(1); // [1] -s.insert(4); // [1, 4] in arbitrary order -s.insert(2); // [1, 4, 2] in arbitrary order -s.insert(1); // [1, 4, 2] in arbitrary order -// the add method did nothing because 1 was already in the set -cout << s.count(1) << endl; // 1 -set.erase(1); // [2, 4] in arbitrary order -cout << s.count(5) << endl; // 0 -s.erase(0); // [2, 4] in arbitrary order -// if the element to be removed does not exist, nothing happens - -for(int element : s){ - cout << element << " "; -} -cout << endl; -// You can iterate through an unordered set, but it will do so in arbitrary order -``` - -### [Ordered Sets](http://www.cplusplus.com/reference/set/set/) - -The second type of set data structure is the ordered or sorted set. Insertions, deletions, and searches on the ordered set require $O(\log n)$ time, based on the number of elements in the set. As well as those supported by the unordered set, the ordered set also allows four additional operations: `begin()`, which returns an iterator to the lowest element in the set, `end()`, which returns an iterator to the highest element in the set, `lower_bound`, which returns an iterator to the least element greater than or equal to some element `k`, and `upper_bound`, which returns an iterator to the least element strictly greater than some element `k`. - -```cpp -set<int> s; -s.insert(1); // [1] -s.insert(14); // [1, 14] -s.insert(9); // [1, 9, 14] -s.insert(2); // [1, 2, 9, 14] -cout << *s.upper_bound(7) << '\n'; // 9 -cout << *s.upper_bound(9) << '\n'; // 14 -cout << *s.lower_bound(5) << '\n'; // 9 -cout << *s.lower_bound(9) << '\n'; // 9 -cout << *s.begin() << '\n'; // 1 -auto it = s.end(); -cout << *(--it) << '\n'; // 14 -s.erase(s.upper_bound(6)); // [1, 2, 14] -``` - -The primary limitation of the ordered set is that we can't efficiently access the $k^{th}$ largest element in the set, or find the number of elements in the set greater than some arbitrary $x$. These operations can be handled using a data structure called an order statistic tree (see Gold - Binary Indexed Trees). - -### [Maps](http://www.cplusplus.com/reference/map/map/) - -A map is a set of ordered pairs, each containing a key and a value. In a map, all keys are required to be unique, but values can be repeated. Maps have three primary methods: one to add a specified key-value pairing, one to retrieve the value for a given key, and one to remove a key-value pairing from the map. Like sets, maps can be unordered (`unordered_map` in C++) or ordered (`map` in C++). In an unordered map, hashing is used to support $O(1)$ operations. In an ordered map, the entries are sorted in order of key. Operations are $O(\log n)$, but accessing or removing the next key higher or lower than some input `k` is also supported. - -### Unordered Maps - -In an unordered map `m`, the `m[key] = value` operator assigns a value to a key and places the key and value pair into the map. The operator `m[key]` returns the value associated with the key. If the key is not present in the map, then `m[key]` is set to 0. The `count(key)` method returns the number of times the key is in the map (which is either one or zero), and therefore checks whether a key exists in the map. Lastly, `erase(key)` and `erase(it)` removes the map entry associated with the specified key or iterator. All of these operations are $O(1)$, but again, due to the hashing, this has a high constant factor. - -```cpp -unordered_map<int, int> m; -m[1] = 5; // [(1, 5)] -m[3] = 14; // [(1, 5); (3, 14)] -m[2] = 7; // [(1, 5); (3, 14); (2, 7)] -m.erase(2); // [(1, 5); (3, 14)] -cout << m[1] << '\n'; // 5 -cout << m.count(7) << '\n' ; // 0 -cout << m.count(1) << '\n' ; // 1 -``` - -### Ordered Maps - -The ordered map supports all of the operations that an unordered map supports, and additionally supports `lower_bound` and `upper_bound`, returning the iterator pointing to the lowest entry not less than the specified key, and the iterator pointing to the lowest entry strictly greater than the specified key respectively. - -```cpp -map<int, int> m; -m[3] = 5; // [(3, 5)] -m[11] = 4; // [(3, 5); (11, 4)] -m[10] = 491; // [(3, 5); (10, 491); (11, 4)] -cout << m.lower_bound(10)->first << " " << m.lower_bound(10)->second << '\n'; // 10 491 -cout << m.upper_bound(10)->first << " " << m.upper_bound(10)->second << '\n'; // 11 4 -m.erase(11); // [(3, 5); (10, 491)] -if (m.upper_bound(10) == m.end()) -{ - cout << "end" << endl; // Prints end -} -``` - -A note on unordered sets and maps: In USACO contests, they're generally fine, but in CodeForces contests, you should always use sorted sets and maps. This is because the built-in hashing algorithm is vulnerable to pathological data sets causing abnormally slow runtimes, in turn causing failures on some test cases (see [neal's blog](https://codeforces.com/blog/entry/62393)). Alternatively, use a different hashing algorithm. - -### [Multisets](http://www.cplusplus.com/reference/set/multiset/) - -Lastly, there is the multiset, which is essentially a sorted set that allows multiple copies of the same element. In addition to all of the regular set operations, the multiset `count()` method returns the number of times an element is present in the multiset. (Actually, you shouldn't use `count()` because this takes time **linear** in the number of matches.) - -The `begin()`, `end()`, `lower_bound()`, and `upper_bound()` operations work the same way they do in the normal sorted set. - -**Warning:** If you want to remove a value *once*, make sure to use `multiset.erase(multiset.find(val))` rather than `multiset.erase(val)`. The latter will remove *all* instances of `val`. - -```cpp -multiset<int> ms; -ms.insert(1); // [1] -ms.insert(14); // [1, 14] -ms.insert(9); // [1, 9, 14] -ms.insert(2); // [1, 2, 9, 14] -ms.insert(9); // [1, 2, 9, 9, 14] -ms.insert(9); // [1, 2, 9, 9, 9, 14] -cout << ms.count(4) << '\n'; // 0 -cout << ms.count(9) << '\n'; // 3 -cout << ms.count(14) << '\n'; // 1 -ms.erase(ms.find(9)); -cout << ms.count(9) << '\n'; // 2 -ms.erase(9); -cout << ms.count(9) << '\n'; // 0 -``` diff --git a/content/3_Bronze/DS.mdx b/content/3_Bronze/DS.mdx deleted file mode 100644 index 3760565..0000000 --- a/content/3_Bronze/DS.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -id: ds -title: Data Structures -author: Nathan Wang, Darren Yao, Benjamin Qi -description: Introductory problems using sets and maps. -prerequisites: - - Bronze - "Built-In C++ Containers" or "Built-In Java Collections" -frequency: 2 ---- - -import { Problem } from "../models" - -export const metadata = { - problems: { - standard: [ - new Problem("YS", "Associative Array", "associative_array", "Intro"), - new Problem("CSES", "Distinct Numbers", "1621", "Intro"), - new Problem("CSES", "Sum of Two Values", "1640", "Intro", false, [], "Can be solved without sets."), - new Problem("CSES", "Concert Tickets", "1091", "Easy", false, ["iterators"]), - new Problem("CSES", "Towers", "1073", "Easy", false, ["multiset", "greedy"]), - new Problem("CSES", "Traffic Lights", "1163", "Normal", false, ["set"]), - new Problem("CSES", "Room Allocation", "1164", "Normal", false, ["multiset", "greedy"]), - ] - } -}; - -## Standard - -Do roughly the first half of the Sorting and Searching section in the [CSES Problem Set](https://cses.fi/problemset/). - -<problems-list problems={metadata.problems.standard} /> diff --git a/content/3_Bronze/Intro_Graphs.mdx b/content/3_Bronze/Intro_Graphs.mdx deleted file mode 100644 index b2e2af0..0000000 --- a/content/3_Bronze/Intro_Graphs.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -id: intro-graphs -title: Introduction to Graphs -author: Darren Yao, Benjamin Qi -description: "?" -frequency: 2 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("Silver", "Grass Planting", "894", "Easy", false, ["tree"]), - new Problem("Bronze", "The Great Revegetation", "916", "Hard", false, []), - new Problem("Bronze", "Milk Factory", "940", "Hard", false, ["tree"]), - new Problem("Bronze", "Swapity Swap", "1013", "Hard", false, ["permutation"], "Hint: One option is to keep swapping until the permutation returns to its original state (but can you do better?)."), - new Problem("Bronze", "Family Tree", "833", "Hard", false, ["tree"]), - ] - } -}; - -<info-block title="Pro Tip"> - -Very important at the Silver level and above, not very frequent in bronze. - -</info-block> - -Graphs can be used to represent many things, from images to wireless signals, but one of the simplest analogies is to a map. Consider a map with several cities and highways connecting the cities. Some problems relating to graphsare: - - - If we have a map with some cities and roads, what's the shortest distance I have to travel to get from point A to point B? - - Consider a map of cities and roads. Is city A connected to city B? Consider a region to be a group of cities such that each city in the group can reach any other city in said group, but no other cities. How many regions are in this map, and which cities are in which region? - -Both of these will be covered in Silver. For now, it suffices to learn how graphs are represented. - -## Tutorial - - - Recommended - - CPH 11 (Basics of graphs) - - Intro to USACO 10.1 - 10.3 - - [PAPS 6.4](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [CSAcademy Graph Intro](https://csacademy.com/lesson/introduction_to_graphs) - - [CSAcademy Graph Representations](https://csacademy.com/lesson/graph_representation) - - Usually, adjacency lists are used. - - Other - - [Topcoder Graphs Pt 1](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-graphs-and-their-data-structures-section-1/) - -## Trees? - -## USACO Bronze Problems - -(what are you expected to know?) - -(add some more) - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/3_Bronze/Intro_Sorting.mdx b/content/3_Bronze/Intro_Sorting.mdx deleted file mode 100644 index 1c2207b..0000000 --- a/content/3_Bronze/Intro_Sorting.mdx +++ /dev/null @@ -1,115 +0,0 @@ ---- -id: intro-sorting -title: "Introduction to Sorting" -author: Siyong Huang, Michael Cao, Nathan Chen -description: Introduces sorting and binary searching on a sorted array. -frequency: 0 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - bubble: [ - new Problem("HR", "Bubble Sort", "https://www.hackerrank.com/challenges/ctci-bubble-sort/problem", "Normal", false, [], "O(N^2)"), - new Problem("Silver", "Out of Sorts", "834", "Very Hard", false, []), - ], - cses: [ - new Problem("CSES", "Apartments", "1084", "Normal", false, [], "Sort applicants and apartments, then greedily assign applicants"), - new Problem("CSES", "Ferris Wheel", "1090", "Normal", false, [], "Sort children, keep a left pointer and a right pointer. Each gondola either is one child from the right pointer or two children, one left and one right."), - new Problem("CSES", "Restaurant Customers", "1619", "Normal", false, [], ""), - new Problem("CSES", "Stick Lengths", "1074", "Normal", false, [], "Spoiler: Optimal length is median"), - ], - } -}; - -**Sorting** is exactly what it sounds like: arranging items in some particular order. - -<info-block title="Pro Tip"> -No bronze problem requires sorting, but it can be an alternate solution that is sometimes much easier to implement. -</info-block> - -## Additional Resources - - - CPH 3 (Sorting) - - once again, very good - -## Sorting Algorithms - -(why are these important?) - -There are many sorting algorithms, here are some sources to learn about the popular ones: - -### Tutorial - -<problems-list problems={metadata.problems.bubble} /> - - - [HackerEarth Quicksort](https://www.hackerearth.com/practice/algorithms/sorting/quick-sort/tutorial/) - - expected $O(N\log N)$ - - [HackerEarth Mergesort](https://www.hackerearth.com/practice/algorithms/sorting/merge-sort/tutorial/) - - $O(N\log N)$ - -## Library Sorting - - - C++ - - [std::sort Documentation](https://en.cppreference.com/w/cpp/algorithm/sort) - - [std::stable\_sort documentation](http://www.cplusplus.com/reference/algorithm/stable_sort/) - - [Golovanov399 - C++ Tricks](https://codeforces.com/blog/entry/74684) - - first two related to sorting - - Java - - [Arrays.sort Documentation](https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#sort(java.lang.Object[])) - - [Breaking Java Arrays.sort()](https://codeforces.com/blog/entry/4827) - - no longer works, but see [this one](https://codeforces.com/contest/1324/hacks/625031/test) instead. - - to avoid getting hacked, [shuffle](https://pastebin.com/k6gCRJDv) the array beforehand. - - Python - - [Sorted Documentation](https://docs.python.org/3/howto/sorting.html) - -## Binary Search - -[Binary search](https://en.wikipedia.org/wiki/Binary_search_algorithm) can be used on monotonic (what's that?) functions for a logarithmic runtime. - -Here is a very basic form of binary search: - -> Find an element in a sorted array of size $N$ in $O(\log N)$ time. - -Other variations are similar, such as the following: - -> Given $K$, find the largest element less than $K$ in a sorted array. - -### Tutorial - - - CSES 3.3 - - [CSAcademy Binary Search](https://csacademy.com/lesson/binary_search) - - [Topcoder Binary Search](https://www.topcoder.com/community/data-science/data-science-tutorials/binary-search/) - - [KhanAcademy Binary Search](https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/binary-search) - - [GeeksForGeeks](https://www.geeksforgeeks.org/binary-search/) - -### Library Functions to do Binary Search - -#### Java - - - [Arrays.binarySearch](https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html) - - [Collections.binarySearch](https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html) - -#### C++ - - - [lower_bound](http://www.cplusplus.com/reference/algorithm/lower_bound/) - - [upper_bound](http://www.cplusplus.com/reference/algorithm/upper_bound/) - -## Example (Coordinate Compression) - -Another useful application of sorting is coordinate compression, which takes some points and reassigns them to remove wasted space. Let's consider the USACO Silver problem [Counting Haybales](http://www.usaco.org/index.php?page=viewproblem2&cpid=666): - -> Farmer John has just arranged his $N$ haybales $(1\le N \le 100,000)$ at various points along the one-dimensional road running across his farm. To make sure they are spaced out appropriately, please help him answer $Q$ queries ($1 \le Q \le 100,000$), each asking for the number of haybales within a specific interval along the road. - -However, each of the points are in the range $0 \ldots 1,000,000,000$, meaning you can't store locations of haybales in, for instance, a boolean array. However, let's place all of the locations of the haybales into a list and sort it. - -(fix this part) - -Now, we can map distinct points to smaller integers without gaps. For example, if the haybales existed at positions $[1, 4, 5, 9]$ and queries were $(1, 2)$ and $(4, 6)$, we can place the integers together and map them from $[1, 2, 4, 5, 6, 9] \rightarrow [1, 2, 3, 4, 5, 6]$. This effectively transforms the haybale positions into $[1, 3, 4, 6]$ and the queries into $1, 2$ and $3, 5$. - -By compressing queries and haybale positions, we've transformed the range of points to $0 \ldots N + 2Q$, allowing us to store prefix sums to effectively query for the number of haybales in a range. - -## Problems - -<problems-list problems={metadata.problems.cses} /> diff --git a/content/3_Bronze/Java_Collections.mdx b/content/3_Bronze/Java_Collections.mdx deleted file mode 100644 index a4e973d..0000000 --- a/content/3_Bronze/Java_Collections.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -id: collections -title: Built-In Java Collections -author: Darren Yao -description: Introduces data structures from Java Collections that are frequently used in competitive programming. -frequency: 4 ---- - -A **data structure** determines how data is stored (is it sorted? indexed? what operations does it support?). Each data structure supports some operations efficiently, while other operations are either inefficient or not supported at all. - -Java default [`Collections`](https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html) data structures are designed to store any type of object. However, we usually don't want this; instead, we want our data structures to only store one type of data, like integers, or strings. We do this by putting the desired data type within the `<>` brackets when declaring the data structure, as follows: - -```java -ArrayList<String> list = new ArrayList<String>(); -``` - -This creates an `ArrayList` structure that only stores objects of type `String`. - -For our examples below, we will primarily use the `Integer` data type, but note that you can have Collections of any object type, including `Strings` , other Collections, or user-defined objects. - -Collections data types always contain an `add` method for adding an element to the collection, and a `remove` method which removes and returns a certain element from the collection. They also support the `size()` method, which returns the number of elements in the data structure, and the `isEmpty()` method, which returns `true` if the data structure is empty, and `false` otherwise. - -## Dynamic Arrays - -You're probably already familiar with regular (static) arrays. Now, there are also dynamic arrays (`ArrayList` in Java) that support all the functions that a normal array does, and can resize itself to accommodate more elements. In a dynamic array, we can also add and delete elements at the end in $O(1)$ time. - -For example, the following code creates a dynamic array and adds the numbers $1\ldots 10$ to it: - -```java -ArrayList<Integer> list = new ArrayList<Integer>(); -for(int i = 1; i <= 10; i++){ - list.add(i); -} -``` - -When declaring a dynamic array we can give it an initial size, so it doesn't resize itself as we add elements to it. The following code initializes an `ArrayList` with initial size $30$: - -```java -ArrayList<Integer> list = new ArrayList<Integer>(30); -``` - -However, we need to be careful that we only add elements to the end of the `ArrayList`; insertion and deletion in the middle of the `ArrayList` is $O(n)$. - -```java -ArrayList<Integer> list = new ArrayList<Integer>(); -list.add(2); // [2] -list.add(3); // [2, 3] -list.add(7); // [2, 3, 7] -list.add(5); // [2, 3, 7, 5] -list.set(1, 4); // sets element at index 1 to 4 -> [2, 4, 7, 5] -list.remove(1); // removes element at index 1 -> [2, 7, 5] -// this remove method is O(n); to be avoided -list.add(8); // [2, 7, 5, 8] -list.remove(list.size()-1); // [2, 7, 5] -// here, we remove the element from the end of the list; this is $O(1)$. -System.out.println(list.get(2)); // 5 -``` - -To iterate through a static or dynamic array, we can use either the regular for loop or the for-each loop. - -```java -ArrayList<Integer> list = new ArrayList<Integer>(); -list.add(1); list.add(7); list.add(4); list.add(5); list.add(2); -int[] arr = {1, 7, 4, 5, 2}; -for(int i = 0; i < list.size(); i++){ // regular - System.out.println(list.get(i)); -} -for(int element : arr){ // for-each - System.out.println(element); -} -``` - -In order to sort a static or dynamic array, use `Arrays.sort(arr)` or `Collections.sort(list)` respectively. The default sort function sorts the array in ascending order. - -In array-based contest problems, we'll use one-, two-, and three-dimensional static arrays most of the time. However, we can also have static arrays of dynamic arrays, dynamic arrays of static arrays, and so on. Usually, the choice between a static array and a dynamic array is just personal preference. - -## Sets & Maps - -A set is a collection of objects that contains no duplicates. There are two types of sets: unordered sets (`HashSet` in Java), and ordered set (`TreeSet` in Java). - -### Unordered Sets - -The unordered set works by hashing, which is assigning a unique code to every variable/object which allows insertions, deletions, and searches in $O(1)$ time, albeit with a high constant factor, as hashing requires a large constant number of operations. However, as the name implies, elements are not ordered in any meaningful way, so traversals of an unordered set will return elements in some arbitrary order. The operations on an unordered set are `add`, which adds an element to the set if not already present, `remove`, which deletes an element if it exists, and `contains`, which checks whether the set contains that element. - -```java -HashSet<Integer> set = new HashSet<Integer>(); -set.add(1); // [1] -set.add(4); // [1, 4] in arbitrary order -set.add(2); // [1, 4, 2] in arbitrary order -set.add(1); // [1, 4, 2] in arbitrary order -// the add method did nothing because 1 was already in the set -System.out.println(set.contains(1)); // true -set.remove(1); // [2, 4] in arbitrary order -System.out.println(set.contains(5)); // false -set.remove(0); // [2, 4] in arbitrary order -// if the element to be removed does not exist, nothing happens - -for(int element : set){ - System.out.println(element); -} -// You can iterate through an unordered set, but it will do so in arbitrary order -``` - -### Ordered Sets - -The second type of set data structure is the ordered or sorted set. Insertions, deletions, and searches on the ordered set require $O(\log n)$ time, based on the number of elements in the set. As well as those supported by the unordered set, the ordered set also allows four additional operations: `first`, which returns the lowest element in the set, `last`, which returns the highest element in the set, `lower`, which returns the greatest element strictly less than some element, and `higher`, which returns the least element strictly greater than it. - -```java -TreeSet<Integer> set = new TreeSet<Integer>(); -set.add(1); // [1] -set.add(14); // [1, 14] -set.add(9); // [1, 9, 14] -set.add(2); // [1, 2, 9, 14] -System.out.println(set.higher(7)); // 9 -System.out.println(set.higher(9)); // 14 -System.out.println(set.lower(5)); // 2 -System.out.println(set.first()); // 1 -System.out.println(set.last()); // 14 -set.remove(set.higher(6)); // [1, 2, 14] -System.out.println(set.higher(23); // ERROR, no such element exists -``` - -The primary limitation of the ordered set is that we can't efficiently access the kth largest element in the set, or find the number of elements in the set greater than some arbitrary x. These operations can be handled using a data structure called an order statistic tree, but that is beyond the scope of this book. - -### Maps - -A map is a set of ordered pairs, each containing a key and a value. In a map, all keys are required to be unique, but values can be repeated. Maps have three primary methods: one to add a specified key-value pairing, one to retrieve the value for a given key, and one to remove a key-value pairing from the map. Like sets, maps can be unordered (`HashSet` in Java) or ordered (`TreeSet` in Java). In an unordered map, hashing is used to support $O(1)$ operations. In an ordered map, the entries are sorted in order of key. Operations are O(\log n), but accessing or removing the next key higher or lower than some input `k` is also supported. - - -### Unordered Maps - -In the unordered map, the `put(key, value)` method assigns a value to a key and places the key and value pair into the map. The `get(key)` method returns the value associated with the key. The `containsKey(key)` method checks whether a key exists in the map. Lastly, `remove(key)` removes the map entry associated with the specified key. All of these operations are $O(1)$, but again, due to the hashing, this has a high constant factor. - -```java -HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); -map.put(1, 5); // [(1, 5)] -map.put(3, 14); // [(1, 5); (3, 14)] -map.put(2, 7); // [(1, 5); (3, 14); (2, 7)] -map.remove(2); // [(1, 5); (3, 14)] -System.out.println(map.get(1)); // 5 -System.out.println(map.containsKey(7)); // false -System.out.println(map.containsKey(1)); // true -``` - - -### Ordered Maps - -The ordered map supports all of the operations that an unordered map supports, and additionally supports `firstKey` / `firstEntry` and `lastKey` /` lastEntry`, returning the lowest key/entry and the highest key/entry, as well as `higherKey` /` higherEntry` and `lowerKey` / `lowerEntry `, returning the lowest key/entry strictly higher than the specified key, or the highest key/entry strictly lower than the specified key. - -```java -TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); -map.put(3, 5); // [(3, 5)] -map.put(11, 4); // [(3, 5); (11, 4)] -map.put(10, 491); // [(3, 5); (10, 491); (11, 4)] -System.out.println(map.firstKey()); // 3 -System.out.println(map.firstEntry()); // (3, 5) -System.out.println(map.lastEntry()); // (11, 4) -System.out.println(map.higherEntry(4)); // (10, 491) -map.remove(11); // [(3, 5); (10, 491)] -System.out.println(map.lowerKey(4)); // 3 -System.out.println(map.lowerKey(3)); // ERROR -``` - -A note on unordered sets and maps: In USACO contests, they're generally fine, but in CodeForces contests, you should always use sorted sets and maps. This is because the built-in hashing algorithm is vulnerable to pathological data sets causing abnormally slow runtimes, in turn causing failures on some test cases. - -### Multisets - -Lastly, there is the multiset, which is essentially a sorted set that allows multiple copies of the same element. While there is no `Multiset` in Java, we can implement one using the `TreeMap` from values to their respective frequencies. We declare the `TreeMap` implementation globally so that we can write functions for adding and removing elements from it. - -```java -static TreeMap<Integer, Integer> multiset = new TreeMap<Integer, Integer>(); - -public static void main(String[] args){ - ... -} - -static void add(int x){ - if(multiset.containsKey(x)){ - multiset.put(x, multiset.get(x) + 1); - } else { - multiset.put(x, 1); - } -} - -static void remove(int x){ - multiset.put(x, multiset.get(x) - 1); - if(multiset.get(x) == 0){ - multiset.remove(x); - } -} -``` - -The first, last, higher, and lower operations still function as intended; just use `firstKey`, `lastKey`, `higherKey`, and `lowerKey` respectively. diff --git a/content/3_Bronze/Pairs_Tuples.mdx b/content/3_Bronze/Pairs_Tuples.mdx deleted file mode 100644 index ced80f3..0000000 --- a/content/3_Bronze/Pairs_Tuples.mdx +++ /dev/null @@ -1,66 +0,0 @@ ---- -id: pairs-tuples -title: Pairs & Tuples -author: Aaron Chew, Benjamin Qi, Nathan Wang, Darren Yao -description: Introduces pairs, which allow you to store two objects (possibly of different types) as a single unit, as well as tuples, which are a generalization of pairs. -frequency: 0 ---- - -(never needed but uh it's pretty useful lol) - -## Pairs - -## C++ - -### [Pair](http://www.cplusplus.com/reference/utility/pair/pair/) - - - `make_pair(a, b)`: Returns a pair with values a, b. - - `pair.first`: The first value of the pair. - - `pair.second`: The second value of the pair. - -Of course, we can hold more than two values with something like `pair<int,pair<int,int>>`. - -Example: - -```cpp -#include <iostream> - -using namespace std; - -int main() { - pair<string, int> myPair = make_pair("Testing", 123); - cout << myPair.first << " " << myPair.second << endl; // Testing 123 -} - -/* Output - * Testing 123 - */ -``` - -### Tuple - -(anyone use?) - -## Java - -Unfortunately there is no default class for pairs in java, so you have to create your own. - -```java -class pair implements Comparable <pair>{ - int first; - int second; - public int compareTo(pair other) { - return first==other.first?second-other.second:first-other.first; - } - pair(int a, int b) { - first=a; - second=b; - } -} - -``` -If you have an array ```pair[]arr=new pair[100]```, make sure each element of this array is not null. You can call ```Arrays.sort(arr);``` on this array and it will sort it by all the ```first``` of the array and if there is a tie in ```first``` it will sort by ```second```. - -You can also use an `int[]` as a pair, or if you're using a pair of two different types, you can use `Object[]`. - -If you want to sort by 3 or more elements, it's a simple change. Just add a new variable in ```pair``` and make sure the comparable compares in the right order you want. diff --git a/content/3_Bronze/Rect_Geo.mdx b/content/3_Bronze/Rect_Geo.mdx deleted file mode 100644 index 9427260..0000000 --- a/content/3_Bronze/Rect_Geo.mdx +++ /dev/null @@ -1,168 +0,0 @@ ---- -id: rect-geo -title: "Rectangle Geometry" -author: Darren Yao, Michael Cao, Benjamin Qi -description: "Geometry problems on USACO Bronze are usually quite simple and limited to intersections and unions of squares or rectangles." -frequency: 2 ---- -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("Bronze", "Square Pasture", "663", "Intro", false, ["rect"]), - new Problem("Bronze", "Blocked Billboard II", "783", "Easy", false, ["rect"]), - new Problem("CF", "Div. 3 C - White Sheet", "contest/1216/problem/C", "Normal", false, ["rect"],"See this code (TODO; codeforces is down) for a nice implementation using the Java Rectangle class."), - ] - } -}; - - -Most only include two or three squares or rectangles, in which case you can simply draw out cases on paper. This should logically lead to a solution. - -## Example: [Blocked Billboard](http://usaco.org/index.php?page=viewproblem2&cpid=759) - -### Naive Solution - -Since all coordinates are in the range $[-1000,1000]$, we can simply go through each of the $2000^2$ possible visible squares and check which ones are visible using nested for loops. - -<spoiler title="Nested Loops"> - -```cpp -#include <bits/stdc++.h> -using namespace std; - -bool ok[2000][2000]; - -int main() { - freopen("billboard.in","r",stdin); - freopen("billboard.out","w",stdout); - for (int i = 0; i < 3; ++i) { - int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; - x1 += 1000, y1 += 1000, x2 += 1000, y2 += 1000; - for (int x = x1; x < x2; ++x) - for (int y = y1; y < y2; ++y) { - if (i < 2) ok[x][y] = 1; - else ok[x][y] = 0; - } - } - int ans = 0; - for (int x = 0; x < 2000; ++x) - for (int y = 0; y < 2000; ++y) - ans += ok[x][y]; - cout << ans << "\n"; -} -``` -</spoiler> - -Of course, this wouldn't suffice if the coordinates were up to $10^9$. - -### Rectangle Class (Java) - -A useful class in `Java` for dealing with rectangle geometry problems (though probably overkill) is the built-in [`Rectangle`](https://docs.oracle.com/javase/8/docs/api/java/awt/Rectangle.html) class. To create a new rectangle, use the following constructor: - -```java -//creates a rectangle with upper-left corner at (x,y) with a specified width and height -Rectangle newRect = new Rectangle(x, y, width, height); -``` - -The `Rectangle` class supports numerous useful methods. - - - `firstRect.intersects(secondRect)` checks if two rectangles intersect. - - - `firstRect.union(secondRect)` returns a rectangle representing the union of two rectangles. - - - `firstRect.contains(x, y)` checks whether the integer point (x,y) exists in firstRect. - - - `firstRect.intersection(secondRect)` returns a rectangle representing the intersection of two rectangles. - - what happens when intersection is empty? - -This class can often lessen the implementation needed in a lot of bronze problems and CodeForces problems. - -For example, here is a nice implementation of the problem ([editorial](http://www.usaco.org/current/data/sol_billboard_bronze_dec17.html)). - -<spoiler title="Java Solution"> - -```java -import java.awt.Rectangle; //needed to use Rectangle class -import java.io.*; -import java.util.*; - -public class blockedBillboard{ - public static void main(String[] args) throws IOException{ - Scanner sc = new Scanner(new File("billboard.in")); - PrintWriter pw = new PrintWriter(new FileWriter("billboard.out")); - int x1, y1, x2, y2; - - //the top left point is (0,0), so you need to do -y2 - - x1 = sc.nextInt(); y1 = sc.nextInt(); x2 = sc.nextInt(); y2 = sc.nextInt(); - Rectangle firstRect = new Rectangle(x1, -y2, x2-x1, y2-y1); - - x1 = sc.nextInt(); y1 = sc.nextInt(); x2 = sc.nextInt(); y2 = sc.nextInt(); - Rectangle secondRect = new Rectangle(x1, -y2, x2-x1, y2-y1); - - x1 = sc.nextInt(); y1 = sc.nextInt(); x2 = sc.nextInt(); y2 = sc.nextInt(); - Rectangle truck = new Rectangle(x1, -y2, x2-x1, y2-y1); - - long firstIntersect = getArea(firstRect.intersection(truck)); - long secondIntersect = getArea(secondRect.intersection(truck)); - - pw.println(getArea(firstRect) + getArea(secondRect) - - firstIntersect - secondIntersect); - pw.close(); - } - public static long getArea(Rectangle r){ - if(r.getWidth() <= 0 || r.getHeight() <= 0){ - return 0; - } - return (long)r.getHeight() * (long)r.getWidth(); - } -} -``` -</spoiler> - -### Rectangle Class (C++) - -Unfortunately, C++ doesn't have a built in rectangle class, so you need to write the functions yourself. Here is the solution to Blocked Billboard written in C++ (thanks, Brian Dean!). - -<spoiler title="C++ Solution"> - -```cpp -#include <iostream> -#include <fstream> -using namespace std; - -struct Rect{ - int x1, y1, x2, y2; -}; - -int area(Rect r){ - return (r.y2 - r.y1) * (r.x2 - r.x1); -} - -int intersect(Rect p, Rect q){ - int xOverlap = max(0, min(p.x2, q.x2) - max(p.x1, q.x1)); - int yOverlap = max(0, min(p.y2, q.y2) - max(p.y1, q.y1)); - return xOverlap * yOverlap; -} - -int main(){ - ifstream cin ("billboard.in"); - ofstream cout ("billboard.out"); - - Rect a, b, t; // billboards a, b, and the truck - - cin >> a.x1 >> a.y1 >> a.x2 >> a.y2; - cin >> b.x1 >> b.y1 >> b.x2 >> b.y2; - cin >> t.x1 >> t.y1 >> t.x2 >> t.y2; - - cout << area(a) + area(b) - intersect(a, t) - intersect(b, t); -} - -``` -</spoiler> - -## Problems - -<problems-list problems={metadata.problems.general} /> diff --git a/content/3_Bronze/Time_Comp.mdx b/content/3_Bronze/Time_Comp.mdx deleted file mode 100644 index d707314..0000000 --- a/content/3_Bronze/Time_Comp.mdx +++ /dev/null @@ -1,149 +0,0 @@ ----
-id: time-comp
-title: "Time Complexity"
-author: Darren Yao, Benjamin Qi
-description: Measuring how long your algorithm takes to run in terms of the input size.
----
-
-## Additional Resources
-
- - CPH 2 (Time Complexity)
- - [PAPS 5](https://www.csc.kth.se/~jsannemo/slask/main.pdf)
-
-# Time Complexity
-
-In programming contests, your program needs to finish running within a certain timeframe in order to receive credit. For USACO, this limit is $4$ seconds for Java submissions. A conservative estimate for the number of operations the grading server can handle per second is $10^8$ (but could be closer to $5 \cdot 10^8$ given good constant factors).
-
-(define time complexity?)
-
-## Complexity Calculations
-
-We want a method of how many operations it takes to run each algorithm, in terms of the input size $n$. Fortunately, this can be done relatively easily using [Big O Notation](https://en.wikipedia.org/wiki/Big_O_notation), which expresses worst-case time complexity as a function of $n$ as $n$ gets arbitrarily large. Complexity is an upper bound for the number of steps an algorithm requires as a function of the input size. In Big O notation, we denote the complexity of a function as $O(f(n))$, where $f(n)$ is a function without constant factors or lower-order terms. We'll see some examples of how this works, as follows.
-
-(ben - formal definition of big O? constant factors don't **have** to be omitted)
-
-The following code is $O(1)$, because it executes a constant number of operations.
-
-```cpp
-int a = 5;
-int b = 7;
-int c = 4;
-int d = a + b + c + 153;
-```
-
-Input and output operations are also assumed to be $O(1)$.
-
-In the following examples, we assume that the code inside the loops is $O(1)$.
-
-The time complexity of loops is the number of iterations that the loop runs. For example, the following code examples are both $O(n)$.
-
-```cpp
-for(int i = 1; i <= n; i++){
- // constant time code here
-}
-```
-
-```cpp
-int i = 0;
-while(i < n){
- // constant time node here
- i++;
-}
-```
-
-Because we ignore constant factors and lower order terms, the following examples are also $O(n)$:
-
-```cpp
-for(int i = 1; i <= 5*n + 17; i++){
- // constant time code here
-}
-```
-
-```cpp
-for(int i = 1; i <= n + 457737; i++){
- // constant time code here
-}
-```
-
-We can find the time complexity of multiple loops by multiplying together the time complexities of each loop. This example is $O(nm)$, because the outer loop runs $O(n)$ iterations and the inner loop $O(m)$.
-
-```cpp
-for(int i = 1; i <= n; i++){
- for(int j = 1; j <= m; j++){
- // constant time code here
- }
-}
-```
-
-In this example, the outer loop runs $O(n)$ iterations, and the inner loop runs anywhere between $1$ and $n$ iterations (which is a maximum of $n$). Since Big O notation calculates worst-case time complexity, we must (?) take the factor of $n$ from the inner loop. Thus, this code is $O(n^2)$.
-
-```cpp
-for(int i = 1; i <= n; i++){
- for(int j = i; j <= n; j++){
- // constant time code here
- }
-}
-```
-
-If an algorithm contains multiple blocks, then its time complexity is the worst time complexity out of any block. For example, the following code is $O(n^2)$.
-
-```cpp
-for(int i = 1; i <= n; i++){
- for(int j = 1; j <= n; j++){
- // constant time code here
- }
-}
-for(int i = 1; i <= n + 58834; i++){
- // more constant time code here
-}
-```
-
-The following code is $O(n^2 + nm)$, because it consists of two blocks of complexity $O(n^2)$ and $O(nm)$, and neither of them is a lower order function with respect to the other.
-
-```cpp
-for(int i = 1; i <= n; i++){
- for(int j = 1; j <= n; j++){
- // constant time code here
- }
-}
-for(int i = 1; i <= n; i++){
- for(int j = 1; j <= m; j++){
- // more constant time code here
- }
-}
-```
-
-## Common Complexities and Constraints
-
-Complexity factors that come from some common algorithms and data structures are as follows:
-
-- Mathematical formulas that just calculate an answer: $O(1)$
-- Unordered set/map: $O(1)$ per operation
-- Binary search: $O(\log n)$
-- Ordered set/map or priority queue: $O(\log n)$ per operation
-- Prime factorization of an integer, or checking primality or compositeness of an integer naively: $O(\sqrt{n})$
-- Reading in $n$ items of input: $O(n)$
-- Iterating through an array or a list of $n$ elements: $O(n)$
-- Sorting: usually $O(n \log n)$ for default sorting algorithms (mergesort, for example `Collections.sort` or `Arrays.sort` on objects)
-- Java Quicksort `Arrays.sort` function on primitives: $O(n^2)$
- - on pathological worst-case data sets, don't use this in CodeForces rounds
-- Iterating through all subsets of size $k$ of the input elements: $O(n^k)$. For example, iterating through all triplets is $O(n^3)$.
-- Iterating through all subsets: $O(2^n)$
-- Iterating through all permutations: $O(n!)$
-
-
-Here are conservative upper bounds on the value of $n$ for each time complexity. You can probably get away with more than this, but this should allow you to quickly check whether an algorithm is viable.
-
-| $n$ | Possible complexities |
-| --------------------- | ----------------------------------- |
-| $n \le 10$ | $O(n!)$, $O(n^7)$, $O(n^6)$ |
-| $n \le 20$ | $O(2^n \cdot n)$, $O(n^5)$ |
-| $n \le 80$ | $O(n^4)$ |
-| $n \le 400$ | $O(n^3)$ |
-| $n \le 7500$ | $O(n^2)$ |
-| $n \le 7 \cdot 10^4$ | $O(n \sqrt n)$ |
-| $n \le 5 \cdot 10^5$ | $O(n \log n)$ |
-| $n \le 5 \cdot 10^6$ | $O(n)$ |
-| $n \le 10^{18}$ | $O(\log^2 n)$, $O(\log n)$, $O(1)$ |
-
-
diff --git a/content/3_Silver/2P.mdx b/content/3_Silver/2P.mdx new file mode 100644 index 0000000..39b27a7 --- /dev/null +++ b/content/3_Silver/2P.mdx @@ -0,0 +1,40 @@ +--- +id: 2P +title: "Two Pointers" +author: Darren Yao +prerequisites: +description: "Refers to iterating two monotonic pointers across an array to search for a pair of indices satisfying some condition in linear time." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Sum of Two Values", "1141", "Easy", false, []), + new Problem("CSES", "Subarray Sums I", "1660", "Easy", false, [], ""), + ], + ad: [ + new Problem("CSES", "Sum of Three Values", "1641", "Easy", false, [], ""), + new Problem("Silver", "Diamond Collector", "643", "Easy", false, ["2P", "Sorting"], ""), + new Problem("CF", "Books", "problemset/problem/279/B", "Easy", false, ["2P"]), + new Problem("CF", "Cellular Network", "problemset/problem/702/C", "Easy", false, []), + new Problem("CF", "USB vs. PS/2", "problemset/problem/762/B", "Easy", false, []), + new Problem("Silver", "Sleepy Cow Herding", "918", "Normal", false, ["2P", "Sorting"], ""), + new Problem("CF", "Garland", "problemset/problem/814/C", "Normal", false, []), + new Problem("CF", "Jury Meeting", "problemset/problem/853/B", "Normal", false, []), + ], +}; + +<Problems problems={problems.sample} /> + +## Resources + +<Resources> + <Resource source="CPH" title="8.1 - Two Pointers" starred>solutions to the problems above</Resource> + <Resource source="IUSACO" title="14.1 - Two Pointers"></Resource> +</Resources> + +## Problems + +<Problems problems={problems.ad} />
\ No newline at end of file diff --git a/content/3_Silver/Binary_Search_Ans.mdx b/content/3_Silver/Binary_Search_Ans.mdx new file mode 100644 index 0000000..53a022c --- /dev/null +++ b/content/3_Silver/Binary_Search_Ans.mdx @@ -0,0 +1,265 @@ +--- +id: binary-search-ans +title: "Binary Search on the Answer" +author: Darren Yao, Abutalib Namazov +prerequisites: + - binary-search-sorted +description: "An extension on binary search to search beyond an array, and rather through possible answers." +frequency: 3 +--- + +import { Problem } from "../models"; + +export const problems = { + ex: [ + new Problem("CF", "Div 2 C - Maximum Median", "contest/1201/problem/C", "Easy", false, [], ""), + ], + usaco: [ + 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("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", "Hard", false, [], ""), + ], + general: [ + new Problem("CSES", "Factory Machines", "1620", "Easy", false, [], "binary search on time and calculate the total products for this time"), + new Problem("CSES", "Array Division", "1085", "Easy", false, [], ""), + new Problem("CF", "Edu C: Magic Ship", "problemset/problem/1117/C", "Normal", false, ["Binary Search", "Prefix Sums"], "binary search on number of the days and then counting full cycles and extra days with prefix sums"), + new Problem("CF", "The Meeting Place Cannot Be Changed", "contest/782/problem/B", "Normal", false, [], "binary search on time with epsilon and then find if there exist non-empty intersection of the ranges (maximum of left points and minimum of right points)"), + new Problem("CF", "Preparing for Merge Sort", "contest/847/problem/B", "Normal", false, [], "process numbers from left to right, keep track of the last element of each block and use binary search of lower_bound to find which block the current numbers belongs"), + new Problem("CF", "Office Keys", "problemset/problem/830/A", "Normal", false, [], "notice that people will take a subarray of length n of keys. so it's possible to fix one position and binary search on another."), + new Problem("CF", "Level Generation", "problemset/problem/818/F", "Hard", false, [], "first find out which is the best way to construct the graph, then it's possible to see that the number of edges increase for some range and then decrease; so, using binary search find the last i such that f(i-1)<=f(i)"), + new Problem("CF", "Packmen", "contest/847/problem/E", "Hard", false, [], "binary search on time and check if packmen can eat all keeping left and right endpoints"), + ], +}; + +<Resources> + <Resource source="IUSACO" title="12 - Binary Search">module is based off this</Resource> +</Resources> + +<br /> + +When we binary search on an answer, we start with a search space of size $N$ which we know the answer lies in. Then each iteration of the binary search cuts the search space in half, so the algorithm tests $O(\log N)$ values. This is efficient and much better than testing each possible value in the search space. + +Let's say we have a function `check(x)` that returns `true` if the answer of $x$ is possible, and `false` otherwise. Usually, in such problems, we want to find the maximum or minimum value of $x$ such that `check(x)` is true. Similarly to how binary search on an array only works on a sorted array, binary search on the answer only works if the answer function is [monotonic](https://en.wikipedia.org/wiki/Monotonic_function), meaning that it is always non-decreasing or always non-increasing. + +In particular, if we want to find the maximum `x` such that `check(x)` is true, then we can binary search if `check(x)` satisfies both of the following conditions: + + - If `check(x)` is `true`, then `check(y)` is true for all $y \leq x$. + - If `check(x)` is `false`, then `check(y)` is false for all $y \geq x$. + +For example, the last point at which the condition below is satisfied is 5. + +``` +check(1) = true +check(2) = true +check(3) = true +check(4) = true +check(5) = true +check(6) = false +check(7) = false +check(8) = false +``` + +If instead we're looking for the minimum `x` that satisfies some condition, then we can binary search if `check(x)` satisfies both of the following conditions: + + - If `check(x)` is true, then `check(y)` is true for all $y \geq x$. + - If `check(x)` is false, then `check(y)` is false for all $y \leq x$. + +The binary search function for this is very similar. We will need to do the same thing, but when the condition is satisfied, we will cut the right part, and when it's not, the left part will be cut. + +Below, we present several algorithms for binary search, which search for the maximum `x` such that `check(x)` is true. + +```cpp +int l = range_start, r = range_end, res = -1; +while(l <= r) { + int mid = (l + r) / 2; // find the middle of the current range + if (check(mid) == true) { + // if mid works, then all numbers smaller than mid also work + // so we only care about the part after mid + res = mid; // update the answer + l = mid + 1; // cut the part before mid + // notice that we already handled mid itself so we cut it as well + } else { + // if mid does not work, greater values would not work too + // so we don't care about them + r = mid - 1; // cut mid and after + } +} +// now res is the answer +// if res is still -1 that means no value in the range satisfies the condition +``` + +Here is a second implementation from Benq: + +```cpp +int fstTrue(function<bool(int)> f, int lo, int hi) { + hi ++; assert(lo <= hi); // assuming f is increasing + while (lo < hi) { // find first index such that f is true + int mid = (lo+hi)/2; // returns hi+1 if no index exists + f(mid) ? hi = mid : lo = mid+1; + } + return lo; +} + +int main() { + cout << fstTrue([](int x) { return 1; },2,10)) << "\n"; // 2 + cout << fstTrue([](int x) { return x >= 5; },2,10)) << "\n"; // 5 + cout << fstTrue([](int x) { return x >= 20; },2,10)) << "\n"; // 11 +} +``` + +(todo: remove one of these) + +<IncompleteSection /> + +There is also another approach to binary searching based on interval jumping. Essentially, we start from the beginning of the array, make jumps, and reduce the jump length as we get closer to the target element. + +<LanguageSection> + +<CPPSection> + +```cpp +long long search(){ + long long pos = 0; long long max = 2E9; + for(long long a = max; a >= 1; a /= 2){ + while(check(pos+a)) pos += a; + } + return pos; +} +``` + +</CPPSection> + +<JavaSection> + +```java +static long search(){ + long pos = 0; long max = (long)2E9; + for(long a = max; a >= 1; a /= 2){ + while(check(pos+a)) pos += a; + } + return pos; +} +``` + +</JavaSection> + +</LanguageSection> + +## Example: Maximum Median + +<Problems problems={problems.ex} /> + +**Statement:** Given an array $\texttt{arr}$ of $n$ integers, where $n$ is odd, we can perform the following operation on it $k$ times: take any element of the array and increase it by $1$. We want to make the median of the array as large as possible after $k$ operations. + +**Constraints:** $1 \leq n \leq 2 \cdot 10^5, 1 \leq k \leq 10^9$ and $n$ is odd. + +**Solution:** We first sort the array in ascending order. Then, we binary search for the maximum possible median. We know that the number of operations required to raise the median to $x$ increases monotonically as $x$ increases, so we can use binary search. For a given median value $x$, the number of operations required to raise the median to $x$ is + +$$ +\sum_{i=(n+1)/2}^{n} \max(0, x - \texttt{arr}[i]). +$$ + +If this value is less than or equal to $k$, then $x$ can be the median, so our check function returns `true`. Otherwise, $x$ cannot be the median, so our check function returns `false`. + +The solution codes use the jump implementation of binary search. + +<LanguageSection> + +<CPPSection> + +```cpp +int n; +long long k; +vector<long long> v; + +// checks whether the number of given operations is sufficient +// to raise the median of the array to x +bool check(long long x){ + long long operationsNeeded = 0; + for(int i = (n-1)/2; i < n; i++){ + operationsNeeded += max(0, x-v[i]); + } + if(operationsNeeded <= k) return true; + else return false; +} + +// binary searches for the correct answer +long long search(){ + long long pos = 0; long long max = 2E9; + for(long long a = max; a >= 1; a /= 2){ + while(check(pos+a)) pos += a; + } + return pos; +} + +int main() { + cin >> n >> k; + for(int i = 0; i < n; i++){ + int t; + cin >> t; + v.push_back(t); + } + sort(v.begin(), v.end()); + + cout << search() << '\n'; +} +``` + +</CPPSection> + +<JavaSection> + +```java +static int n; +static long k; +static long[] arr; +public static void main(String[] args) { + + n = r.nextInt(); k = r.nextLong(); + arr = new long[n]; + for(int i = 0; i < n; i++){ + arr[i] = r.nextLong(); + } + Arrays.sort(arr); + + pw.println(search()); + pw.close(); +} + +// binary searches for the correct answer +static long search(){ + long pos = 0; long max = (long)2E9; + for(long a = max; a >= 1; a /= 2){ + while(check(pos+a)) pos += a; + } + return pos; +} + +// checks whether the number of given operations is sufficient +// to raise the median of the array to x +static boolean check(long x){ + long operationsNeeded = 0; + for(int i = (n-1)/2; i < n; i++){ + operationsNeeded += Math.max(0, x-arr[i]); + } + if(operationsNeeded <= k){ return true; } + else{ return false; } +} +``` + +</JavaSection> + +</LanguageSection> + + +## USACO Problems + +<Problems problems={problems.usaco} /> + +## General Problems + +<Problems problems={problems.general} /> diff --git a/content/3_Silver/Binary_Search_Sorted.mdx b/content/3_Silver/Binary_Search_Sorted.mdx new file mode 100644 index 0000000..0e09690 --- /dev/null +++ b/content/3_Silver/Binary_Search_Sorted.mdx @@ -0,0 +1,80 @@ +--- +id: binary-search-sorted +title: "Binary Search on a Sorted Array" +author: Siyong Huang, Michael Cao, Nathan Chen +description: "Quickly finding elements in a sorted array." +prerequisites: + - intro-ds +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + bubble: [ + new Problem("HR", "Bubble Sort", "https://www.hackerrank.com/challenges/ctci-bubble-sort/problem", "Easy", false, [], "O(N^2)"), + new Problem("Silver", "Out of Sorts", "834", "Very Hard", false, []), + ], + count: [ + new Problem("Silver", "Counting Haybales", "666", "Normal", false, []), + ], +}; + +Suppose that we want to find an element in a sorted array of size $N$ in $O(\log N)$ time. We can do this with [**binary search**](https://en.wikipedia.org/wiki/Binary_search_algorithm); each iteration of the binary search cuts the search space in half, so the algorithm tests $O(\log N)$ values. This is efficient and much better than testing every element in an array. + +<Resources> + <Resource source="KA" title="Binary Search" url="https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/binary-search" starred>animations!</Resource> + <Resource source="CSA" title="Binary Search" url="binary_search" starred>animation!</Resource> + <Resource source="CPH" title="3.3 - Binary Search" starred></Resource> + <Resource source="TC" title="Binary Search" url="binary-search"></Resource> + <Resource source="GFG" title="Binary Search" url="binary-search"></Resource> +</Resources> + +## Library Functions + +<LanguageSection> + +<CPPSection> + +<Resources> + <Resource source="CPP" url="http://www.cplusplus.com/reference/algorithm/lower_bound/" title="lower_bound"> </Resource> + <Resource source="CPP" url="http://www.cplusplus.com/reference/algorithm/upper_bound/" title="upper_bound"> </Resource> +</Resources> + +</CPPSection> + +<JavaSection> + +<Resources> + <Resource source="JAVA" url="https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#binarySearch(int[],%20int)" title="Arrays.binarySearch"> </Resource> + <Resource source="JAVA" url="https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#binarySearch(java.util.List,%20T)" title="Collections.binarySearch"> </Resource> +</Resources> + +</JavaSection> + +</LanguageSection> + +## Coordinate Compression + +A related topic is **coordinate compression**, which takes some points and reassigns them to remove wasted space. + +<Problems problems={problems.count} /> + +> Farmer John has just arranged his $N$ haybales $(1\le N \le 100,000)$ at various points along the one-dimensional road running across his farm. To make sure they are spaced out appropriately, please help him answer $Q$ queries ($1 \le Q \le 100,000$), each asking for the number of haybales within a specific interval along the road. + +However, each of the points are in the range $0 \ldots 1,000,000,000$, meaning you can't store locations of haybales in, for instance, a boolean array. + +<Spoiler title="Solution"> + +Let's place all of the locations of the haybales into a list and sort it. + +(fix part below so transform to range $1\ldots N$) + +<IncompleteSection /> + +<!-- Now, we can map distinct points to smaller integers without gaps. For example, if the haybales existed at positions $[1, 4, 5, 9]$ and queries were $(1, 2)$ and $(4, 6)$, we can place the integers together and map them from $[1, 2, 4, 5, 6, 9] \rightarrow [1, 2, 3, 4, 5, 6]$. This effectively transforms the haybale positions into $[1, 3, 4, 6]$ and the queries into $1, 2$ and $3, 5$. + +By compressing queries and haybale positions, we've transformed the range of points to $0 \ldots N + 2Q$, allowing us to store prefix sums to effectively query for the number of haybales in a range. + --> + +</Spoiler>
\ No newline at end of file diff --git a/content/3_Silver/Custom_Cpp_STL.mdx b/content/3_Silver/Custom_Cpp_STL.mdx new file mode 100644 index 0000000..0ff54a1 --- /dev/null +++ b/content/3_Silver/Custom_Cpp_STL.mdx @@ -0,0 +1,194 @@ +--- +id: custom-cpp-stl +title: "C++ Sets with Custom Comparators" +author: Siyong Huang, Benjamin Qi +prerequisites: + - sorting-custom + - intro-ordered + - lambda +description: "Incorporating custom comparators into STL objects." +frequency: 1 +--- + +<Resources> + <Resource source="fushar" title="Comparison Functions in C++" starred url="http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/">Covers all of this material.</Resource> + <Resource source="CPP" url="https://en.cppreference.com/w/cpp/container/set" title="Set">reference</Resource> +</Resources> + +<br /> + +What if we want to use a C++ `set` with the `Edge` struct that was defined in [Sorting with Custom Comparators](./sorting-custom)? + +## Operator Overloading + +Works as expected, although you should make sure to include the second `const` or you'll get a compilation error. From the link above: + +> [The second const] means you cannot modify member variables of the current object. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; + bool operator<(const Edge& y) const { return w < y.w; } +}; + +int main() { + int M = 4; + set<Edge> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.insert({a,b,w}); + } + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +## Comparator + +<Resources> + <Resource source="SO" title="Using custom std::set comparator" url="https://stackoverflow.com/questions/2620862/using-custom-stdset-comparator"> </Resource> +</Resources> + +### With a Function + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; +}; + +bool cmp(const Edge& x, const Edge& y) { return x.w < y.w; } + +int main() { + int M = 4; + set<Edge,bool(*)(const Edge&,const Edge&)> v(cmp); + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.insert({a,b,w}); + } + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +You can also use the following syntax to declare set `v` using a function: + +`set<Edge,decltype(&cmp)> v(cmp);` + +### With Lambda Expressions + +```cpp +auto cmp = [](const Edge& x, const Edge& y) { return x.w < y.w; }; + +int main() { + int M = 4; + set<Edge,bool(*)(const Edge&,const Edge&)> v(cmp); + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.insert({a,b,w}); + } + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +You can also use the following syntax to declare set `v` using a lambda: + +`set<Edge,decltype(cmp)> v(cmp);` + +though `decltype(cmp)` is **not** actually equivalent to `bool(*)(const Edge&,const Edge&)`. + +## Functors + +Probably less confusing than the method above. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; +}; + +struct cmp { + bool operator()(const Edge& x, const Edge& y) { return x.w < y.w; } +}; + +int main() { + int M = 4; + set<Edge,cmp> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.insert({a,b,w}); + } + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +We can also use `cmp` like a normal function by adding `()` after it. + +```cpp +int main() { + int M = 4; + vector<Edge> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({a,b,w}); + } + sort(begin(v),end(v),cmp()); + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +<Info title="Pro Tip"> + +One functor can be used for multiple objects. Just keep overloading the `()` operator! + +</Info> + +## Built-In Functors + +Overloading the less than operator (`<`) automatically generates the functor [`less<Edge>`](https://en.cppreference.com/w/cpp/utility/functional/less). +Similarly, overloading (`>`) automatically generates the functor [`greater<Edge>`](https://en.cppreference.com/w/cpp/utility/functional/greater). +We can use this to store a set in reverse order. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; + bool operator>(const Edge& y) const { return w > y.w; } +}; + +int main() { + int M = 4; + set<Edge,greater<Edge>> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.insert({a,b,w}); + } + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} + +/* Output: +2 3 10 +1 2 9 +1 3 7 +2 4 3 +*/ +``` + +## Other Containers + +The following are all valid: + +```cpp +set<int,greater<int>> a; +map<int,string,greater<int>> b; +priority_queue<int,vector<int>,greater<int>> c; +``` + +Using a custom comparator for priority queues is especially common. Again, a C++ priority queue will pop its largest element by default, while the above code will cause one to pop its smallest element instead.
\ No newline at end of file diff --git a/content/3_Silver/DFS.mdx b/content/3_Silver/DFS.mdx new file mode 100644 index 0000000..1b4fb52 --- /dev/null +++ b/content/3_Silver/DFS.mdx @@ -0,0 +1,269 @@ +--- +id: dfs +title: Depth First Search (DFS) +author: Siyong Huang +prerequisites: + - intro-graphs +description: "A way to recursively traverse a graph." +frequency: 4 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Building Roads", "1666", "Intro|Easy", false, ["DFS"]), + ], + general: [ + new Problem("CF", "Bear & Friendship", "problemset/problem/771/A", "Easy", false, ["DFS"]), + new Problem("Silver", "Closing the Farm", "644", "Easy", false, ["DFS"]), + new Problem("Silver", "Moocast", "668", "Easy", false, ["DFS"]), + new Problem("Silver", "Fence Planning", "944", "Easy", false, ["DFS"]), + new Problem("Kattis", "Birthday Party", "birthday", "Easy", false, ["DFS"], "DFS with each edge removed"), + new Problem("Silver", "Mootube", "788", "Easy", false, ["Tree", "DFS"]), + new Problem("CF", "PolandBall & Forest", "problemset/problem/755/C", "Easy", false, ["Tree", "DFS"]), + new Problem("Silver", "Milk Visits", "968", "Normal", false, ["DFS"]), + 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"]), + new Problem("CF", "Journey", "contest/839/problem/C", "Easy", false, ["Tree", "DFS"]), + new Problem("CSES", "Tree Diameter", "1131", "Normal", false, ["Tree", "DFS"]), + new Problem("CSES", "Tree Distances I", "1132", "Normal", false, ["Tree", "DFS"]), + new Problem("CSES", "Tree Distances II", "1133", "Normal", false, ["Tree", "DFS"]), + new Problem("CF", "Wizard's Tour", "contest/860/problem/D", "Normal", false, ["Tree", "DFS"]), + 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"]), + ], + 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, []), + ], +}; + +## Resources + +<Resources> + <Resource source="CPH" title="12.1 - DFS, 14 - Tree algorithms" starred></Resource> + <Resource source="CSA" title="Depth First Search" url="depth_first_search" starred>interactive</Resource> + <Resource source="IUSACO" title="10.4 - Graph Traversal Algorithms"></Resource> + <Resource source="PAPS" title="12.2 - Depth-First Search">with an example problem</Resource> + <Resource source="CPC" title="7 - Graphs 1" url="07_graphs_1">fast-paced</Resource> + <Resource source="cp-algo" title="Depth First Search" url="graph/depth-first-search.html">hard to parse for a beginner</Resource> + <Resource source="TC" title="Graphs Section 2" url="introduction-to-graphs-and-their-data-structures-section-2"></Resource> +</Resources> + +## Counting Connected Components + +<Problems problems={problems.sample} /> + +### Implementation + +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 /> + +### Problems + +<Problems problems={problems.general} /> + +## Tree Problems + +### Implementation + +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 /> + +### Problems + +<Problems problems={problems.tree} /> + +## Graph Two-Coloring + +*Graph two-coloring* refers to assigning a boolean value to each node of the graph, dictated by the edge configuration. +The most common example of a two-colored graph is a *bipartite graph*, in which each edge connects two nodes of opposite colors. + +<Problems problems={problems.bipsample} /> + +### Resources + +<Resources> + <Resource source="IUSACO" title="10.7 - Bipartite Graphs" starred></Resource> + <Resource source="CPH" title="12.3 - Graph Traversal: Applications"></Resource> + <Resource source="cp-algo" title="Bipartite Check" url="graph/bipartite-check.html">Uses BFS, but DFS accomplishes the same task.</Resource> +</Resources> + + +### Implementation + +The idea is that we can arbitrarily label a node and then run DFS. Every time we visit a new (unvisited) node, we set its color based on the edge rule. When we visit a previously visited node, check to see whether its color matches the edge rule. For example, an implementation of coloring a bipartite graph is shown below. + +<LanguageSection> + +<CPPSection> + +```cpp +//UNTESTED +bool is_bipartite = true; +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); + } +} +``` + +</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 + +<Problems problems={problems.bip} /> diff --git a/content/3_Silver/Flood_Fill.mdx b/content/3_Silver/Flood_Fill.mdx new file mode 100644 index 0000000..b3faa9b --- /dev/null +++ b/content/3_Silver/Flood_Fill.mdx @@ -0,0 +1,211 @@ +--- +id: ff +title: Flood Fill +author: Darren Yao +prerequisites: + - dfs +description: "A variant of DFS which finds connected components in a graph respresented by a grid." +frequency: 3 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("LC", "Flood Fill", "https://leetcode.com/problems/flood-fill/", "Intro|Easy", false, []), + new Problem("CSES", "Counting Rooms", "1192", "Easy", false, []), + ], + general: [ + new Problem("CSES", "Labyrinth", "1193", "Easy", false, []), + new Problem("Silver", "Count Cross", "716", "Easy", false, ["FF"]), + new Problem("Silver", "Ice Perimeter", "895", "Easy", false, []), + new Problem("Silver", "Switching on the Lights", "570", "Normal", false, []), + new Problem("Silver", "Build Gates", "596", "Normal", false, []), + new Problem("Silver", "Milk Pails", "620", "Normal", false, []), + new Problem("Silver", "Where's Bessie?", "740", "Normal", false, []), + new Problem("Silver", "Why Did the Cow Cross the Road III", "716", "Normal", false, []), + new Problem("Silver", "Multiplayer Moo", "836", "Hard", false, []), + new Problem("Silver", "Snow Boots", "811", "Hard", false, []), + new Problem("Silver", "Mooyo Mooyo", "860", "Normal", false, [], ""), + ], +}; + +<!-- Mooyo Mooyo: 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. --> + +<Problems problems={problems.sample} /> + +<br /> + +<Resources> + <Resource source="IUSACO" title="10.5 - Flood Fill">module is based off this</Resource> +</Resources> + +**Flood fill** is an algorithm that identifies and labels the connected component that a +particular cell belongs to, in a multi-dimensional array. Essentially, it’s DFS, but on a grid, +and we want to find the connected component of all the connected cells with the same number. +For example, let’s look at the following grid and see how floodfill works, starting from the +top-left cell. The color scheme will be the same: red for the node currently being processed, +blue for nodes already visited, and uncolored for nodes not yet visited. + +(tables) + +<IncompleteSection /> + +As opposed to an explicit graph where the edges are given, a grid is an implicit graph. +This means that the neighbors are just the nodes directly adjacent in the four cardinal +directions. + +Usually, grids given in problems will be $N$ by $M$, so the first line of the input contains the +numbers $N$ and $M$. In this example, we will use an two-dimensional integer array to store the +grid, but depending on the problem, a two-dimensional character array or a two-dimensional +boolean array may be more appropriate. Then, there are $N$ rows, each with $M$ numbers +containing the contents of each square in the grid. Example input might look like the following +(varies between problems): + +``` +3 4 +1 1 2 1 +2 3 2 1 +1 3 3 3 +``` + +And we’ll want to input the grid as follows: + +```java +static int[][] grid; +static int n, m; + +public static void main(String[] args){ + int n = r.nextInt(); + int m = r.nextInt(); + grid = new int[n][m]; + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + grid[i][j] = r.nextInt(); + } + } +} +``` + +```cpp +int grid[MAXN][MAXM]; +int n, m; + +int main(){ + cin >> n >> m; + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + cin >> grid[i][j]; + } + } + return 0; +} +``` + +## Implementation + +<Resources> + <Resource source="GFG" title="Flood Fill" url="flood-fill-algorithm-implement-fill-paint"></Resource> +</Resources> + +When doing floodfill, we will maintain an $N\times M$ array of booleans to keep track of +which squares have been visited, and a global variable to maintain the size of the current +component we are visiting. Make sure to store the grid, the visited array, dimensions, and +the current size variable globally. + +This means that we want to recursively call the search function from the squares above, +below, and to the left and right of our current square. The algorithm to find the size of a +connected component in a grid using floodfill is as follows (we’ll also maintain a 2D visited array). + +<LanguageSection> + +<CPPSection> + +```cpp +int grid[MAXN][MAXM]; // the grid itself +int n, m; // grid dimensions, rows and columns +bool visited[MAXN][MAXM]; // keeps track of which nodes have been visited +int currentSize = 0; // reset to 0 each time we start a new component + +void floodfill(int r, int c, int color){ + if(r < 0 || r >= n || c < 0 || c >= m) return; // if outside grid + if(grid[r][c] != color) return; // wrong color + if(visited[r][c]) return; // already visited this square + visited[r][c] = true; // mark current square as visited + currentSize++; // increment the size for each square we visit + // recursively call floodfill for neighboring squares + floodfill(r, c+1, color); + floodfill(r, c-1, color); + floodfill(r-1, c, color); + floodfill(r+1, c, color); +} + +int main(){ + /** + * input code and other problem-specific stuff here + */ + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + if(!visited[i][j]){ + currentSize = 0; + floodfill(i, j, grid[i][j]); + // start a floodfill if the square hasn't + // already been visited, and then + // store or otherwise use the component size for whatever + // it's needed for + } + } + } + return 0; +} +``` + +</CPPSection> + +<JavaSection> + +```java +static int[][] grid; // the grid itself +static int n, m; // grid dimensions, rows and columns +static boolean[][] visited; // keeps track of which nodes have been visited +static int currentSize = 0; // reset to 0 each time we start a new component + +public static void main(String[] args){ + /** + * input code and other problem-specific stuff here + */ + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + if(!visited[i][j]){ + currentSize = 0; + floodfill(i, j, grid[i][j]); + // start a floodfill if the square hasn't already been visited, + // and then store or otherwise use the component size + // for whatever it's needed for + } + } + } +} + +static void floodfill(int r, int c, int color){ + if(r < 0 || r >= n || c < 0 || c >= m) return; // if outside grid + if(grid[r][c] != color) return; // wrong color + if(visited[r][c]) return; // already visited this square + visited[r][c] = true; // mark current square as visited + currentSize++; // increment the size for each square we visit + // recursively call floodfill for neighboring squares + floodfill(r, c+1, color); + floodfill(r, c-1, color); + floodfill(r-1, c, color); + floodfill(r+1, c, color); +} +``` + +</JavaSection> + +</LanguageSection> + + +## Problems + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/3_Silver/Func_Graphs.mdx b/content/3_Silver/Func_Graphs.mdx new file mode 100644 index 0000000..e998007 --- /dev/null +++ b/content/3_Silver/Func_Graphs.mdx @@ -0,0 +1,122 @@ +--- +id: func-graphs +title: Functional Graphs +author: Siyong Huang +prerequisites: + - dfs +description: "Properties of functional graphs, directed graphs in which every vertex has exactly one outgoing edge." +frequency: 1 +--- + + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CF", "Div 2 B - Badge", "contest/1020/problem/B", "Very Easy", false, ["Func Graph"], "Try to solve the problem in $O(N)$!"), + ], + general: [ + new Problem("Silver", "The Bovine Shuffle", "764", "Easy", false, ["Func Graph"], "Try to solve the problem in $O(N)$!"), + new Problem("CSES", "Planets Cycles", "1751", "Normal", false, ["Func Graph"], ""), + new Problem("Silver", "Swapity Swapity Swap", "1014", "Normal", false, ["Permutation"], ""), + new Problem("CF", "Cooperative Game", "https://codeforces.com/contest/1137/problem/D", "Normal", false, ["Func Graph, Floyd's Algorithm"], "Implement Floyd's Algorithm! (You only actually need 3 friends to solve the problem!)"), + new Problem("POI", "Mafia", "https://szkopul.edu.pl/problemset/problem/w3YAoAT3ej27YeiaNWjK57_G/site/?key=statement", "Hard", false, ["Func Graph"], ""), + new Problem("POI", "Spies", "https://szkopul.edu.pl/problemset/problem/r6tMTfvQFPAEfQioYMCQndQe/site/?key=statement", "Hard", false, [], ""), + new Problem("POI", "Frog", "https://szkopul.edu.pl/problemset/problem/qDH9CkBHZKHY4vbKRBlXPrA7/site/?key=statement", "Hard", false, [], ""), + new Problem("ojuz", "Space Pirate", "JOI14_space_pirate", "Insane", false, ["Graphs"], "One of the most difficult problems of all time. Build a tree with a single back edge. Break into three cases: path -> other cycle, path -> subtree, path -> non-subtree. Then perform some black magic."), + ], +}; + +## Functional Graphs + +We'll consider graphs like the one presented in this problem: + +<Problems problems={problems.sample} /> + +< br/> + +In a **functional graph**, each node has exactly one out-edge. +This is also commonly referred to as a **successor graph**. + +### Resources + +<Resources> + <Resource source="CPH" title="16.3 Successor Graphs"></Resource> +</Resources> + +### Implementation + +The following sample code counts the number of cycles in such a graph. The "stack" contains nodes that can reach the current node. If the current node points to a node `v` on the stack (`on_stack[v]` is true), then we know that a cycle has been created. However, if the current node points to a node `v` that has been previously visited but is not on the stack, then we know that the current chain of nodes points into a cycle that has already been considered. + +<LanguageSection> + +<CPPSection> + +```cpp +//UNTESTED + +//Each node points to next_node[node] + +bool visited[MAXN], on_stack[MAXN]; +int number_of_cycles = 0, next_node[MAXN]; +void dfs(int n) +{ + visited[n] = on_stack[n] = true; + int u = next_node[n]; + if(on_stack[u]) + number_of_cycles++; + else if(!visited[u]) + dfs(u); + on_stack[n] = false; +} +int main() +{ + //read input, etc + for(int i = 1;i <= N;i++) + if(!visited[i]) + dfs(i); +} +``` + +</CPPSection> + +</LanguageSection> + +## Floyd's Algorithm + +**Floyd's Algorithm**, also commonly referred to as the **Tortoise and Hare Algorithm**, is capable of detecting cycles in a functional graph in $O(N)$ time and $O(1)$ memory (not counting the graph itself) + +<Resources> + <Resource source="CPH" title="16.4 - Cycle Detection" starred></Resource> + <Resource source="Medium" title="The Tortoise and the Hare (Floyd's Algorithm)" url="https://medium.com/@tuvo1106/the-tortoise-and-the-hare-floyds-algorithm-87badf5f7d41"></Resource> +</Resources> + +### Implementation + +```cpp +//UNTESTED +pair<int, int> detect_cycle(int *next, int start_node) //return pair(length of cycle, distance from start node to in cycle) +{ + int t=start_node, h=start_node; //tortoise and hare + do + { + t=next[t], h=next[h]; + } while(t != h) + + int dist=0; + t = start_node; + while(t != h) + ++dist, t=next[t], h=next[h]; + + int len=1; + h = next[t]; + while(h != t) + ++len, h=next[h]; + return make_pair(len, dist); +} +``` + + +### Problems + +<Problems problems={problems.general} /> diff --git a/content/3_Silver/Greedy.mdx b/content/3_Silver/Greedy.mdx new file mode 100644 index 0000000..5fb4129 --- /dev/null +++ b/content/3_Silver/Greedy.mdx @@ -0,0 +1,271 @@ +--- +id: greedy +title: "Greedy Algorithms involving Sorting" +author: Darren Yao +prerequisites: + - intro-greedy + - sorting-custom + - intro-ordered +description: "?" +frequency: 3 +--- + +import {Problem} from "../models" + +export const problems = { + movie: [ + new Problem("CSES", "Movie Festival", "1629", "Easy", false, [], ""), + ], + cses: [ + new Problem("CSES", "Stick Lengths", "1074", "Easy", false, [], "Spoiler: Optimal length is median"), + new Problem("CSES", "Apartments", "1084", "Easy", false, [], "Sort applicants and apartments, then greedily assign applicants"), + new Problem("CSES", "Ferris Wheel", "1090", "Easy", false, [], "Sort children, keep a left pointer and a right pointer. Each gondola either is one child from the right pointer or two children, one left and one right."), + new Problem("CSES", "Towers", "1073", "Easy", false, ["multiset", "greedy"]), + new Problem("CSES", "Room Allocation", "1164", "Normal", false, ["multiset", "greedy"], "similar to above"), + new Problem("CSES", "Tasks & Deadlines", "1630", "Easy", false, [], ""), + new Problem("CSES", "Movie Festival II", "1632", "Easy", false, [], ""), + new Problem("CSES", "Stick Division", "1161", "Hard", false, [], ""), + ], + other: [ + new Problem("Silver", "Paired Up", "738", "Easy", false, ["2P", "Sorting"]), + new Problem("Silver", "Lemonade Line", "835", "Easy", false, ["Sorting"], ""), + new Problem("Silver", "High Card Wins", "571", "Normal", false, [], ""), + new Problem("Gold", "High Card Low Card", "573", "Normal", false, [], ""), + new Problem("Silver", "Berry Picking", "990", "Normal", false, [], ""), + new Problem("Silver", "Why ... (helpcross)", "714", "Normal", false, ["Sorting"], ""), + new Problem("CSA", "Sure Bet", "sure-bet", "Normal", false, [], ""), + ], +}; + +<!-- new Problem("Silver", "Rest Stops", "810", "?", false, [], ""), --> +<!-- new Problem("CF", "Did you Mean...", "contest/860/problem/A", "?", false, [], ""), --> +<!-- new Problem("CF", "Bus", "contest/864/problem/C", "?", false, [], ""), --> +<!-- new Problem("CF", "Permutation", "contest/864/problem/D", "?", false, [], ""), --> +<!-- new Problem("CF", "New Year and Three Musketeers", "contest/611/problem/E", "Hard", false, [], "needs maps"), --> + + +<Resources> + <Resource source="IUSACO" title="9 - Greedy Algorithms">Module is based off this.</Resource> + <Resource source="CPH" title="6 - Greedy Algorithms" starred></Resource> + <Resource source="PAPS" title="8 - Greedy Algorithms"></Resource> + <Resource source="CPC" title="5 - Greedy Algorithms" url="05_greedy_algorithms"></Resource> +</Resources> + +<br /> + +Usually, when using a greedy algorithm, there is a **heuristic** or **value function** that determines which choice is considered most optimal. + +(what's a heuristic or value function?) + +<IncompleteSection /> + +Here, we'll focus on problems where some sorting step is involved. + + +## Example: Studying Algorithms + +Steph wants to improve her knowledge of algorithms over winter break. She has a total of $X$ ($1 \leq X \leq 10^4$) minutes to dedicate to learning algorithms. There are $N$ ($1 \leq N \leq 100$) algorithms, and each one of them requires $a_i$ ($1 \leq a_i \leq 100$) minutes to learn. Find the maximum number of algorithms she can learn. + +### Solution + +<Spoiler title="Solution"> + +The first observation we make is that Steph should prioritize learning algorithms from easiest to hardest; in other words, start with learning the algorithm that requires the least amount of time, and then choose further algorithms in increasing order of time required. Let's look at the following example: + +$$ +X = 15, \qquad N = 6, \qquad a_i = \{ 4, 3, 8, 4, 7, 3 \} +$$ + +After sorting the array, we have $\{ 3, 3, 4, 4, 7, 8 \}$. Within the maximum of 15 minutes, Steph can learn four algorithms in a total of $3+3+4+4 = 14$ minutes. + +The implementation of this algorithm is very simple. We sort the array, and then take as many elements as possible while the sum of times of algorithms chosen so far is less than $X$. Sorting the array takes $O(N \log N)$ time, and iterating through the array takes $O(N)$ time, for a total time complexity of $O(N \log N)$. + +<LanguageSection> + +<CPPSection> + +```cpp +// read in the input, store the algorithms in a vector, algorithms +sort(algorithms.begin(), algorithms.end()); +int count = 0; // number of minutes used so far +int i = 0; +while(count + algorithms[i] <= x){ + // while there is enough time, learn more algorithms + count += algorithms[i]; + i++; +} +cout << i << endl; // print the ans +``` + +</CPPSection> + +<JavaSection> + +```java +// read in the input, store the algorithms in int[] algorithms +Arrays.sort(algorithms); +int count = 0; // number of minutes used so far +int i = 0; +while(count + algorithms[i] <= x){ + // while there is enough time, learn more algorithms + count += algorithms[i]; + i++; +} +pw.println(i); // print the ans +pw.close(); +``` + +</JavaSection> + +</LanguageSection> + +</Spoiler> + +## Example: The Scheduling Problem + +<Problems problems={problems.movie} /> + +There are $N$ events, each described by their starting and ending times. You can only attend one event at a time, and if you choose to attend an event, you must attend the entire event. Traveling between events is instantaneous. What's the maximum number of events you can attend? + +### Bad Greedy: Earliest Starting Next Event + +One possible ordering for a greedy algorithm would always select the next possible event that begins as soon as possible. Let's look at the following example, where the selected events are highlighted in red: + +<svg viewBox="0 0 200 40"> + <line x1="10" y1="5" x2="80" y2="5" style="stroke:rgb(255,0,0);stroke-width:1" /> + <line x1="40" y1="15" x2="120" y2="15" style="stroke:rgb(0,0,0);stroke-width:1" /> + <line x1="110" y1="25" x2="180" y2="25" style="stroke:rgb(255,0,0);stroke-width:1" /> + <line x1="150" y1="35" x2="180" y2="35" style="stroke:rgb(0,0,0);stroke-width:1" /> +</svg> + +In this example, the greedy algorithm selects two events, which is optimal. However, this doesn't always work, as shown by the following counterexample: + +<svg viewBox="0 0 200 40"> + <line x1="10" y1="5" x2="160" y2="5" style="stroke:rgb(255,0,0);stroke-width:1" /> + <line x1="40" y1="15" x2="80" y2="15" style="stroke:rgb(0,0,0);stroke-width:1" /> + <line x1="100" y1="25" x2="135" y2="25" style="stroke:rgb(0,0,0);stroke-width:1" /> + <line x1="150" y1="35" x2="180" y2="35" style="stroke:rgb(0,0,0);stroke-width:1" /> +</svg> + +In this case, the greedy algorithm selects to attend only one event. However, the optimal solution would be the following: + +<svg viewBox="0 0 200 40"> + <line x1="10" y1="5" x2="160" y2="5" style="stroke:rgb(0,0,0);stroke-width:1" /> + <line x1="40" y1="15" x2="80" y2="15" style="stroke:rgb(255,0,0);stroke-width:1" /> + <line x1="100" y1="25" x2="135" y2="25" style="stroke:rgb(255,0,0);stroke-width:1" /> + <line x1="150" y1="35" x2="180" y2="35" style="stroke:rgb(255,0,0);stroke-width:1" /> +</svg> + +### Correct Greedy: Earliest Ending Next Event + +Instead, we can select the event that ends as early as possible. This correctly selects the three events. + +<svg viewBox="0 0 200 40"> + <line x1="10" y1="5" x2="160" y2="5" style="stroke:rgb(0,0,0);stroke-width:1" /> + <line x1="40" y1="15" x2="80" y2="15" style="stroke:rgb(255,0,0);stroke-width:1" /> + <line x1="100" y1="25" x2="135" y2="25" style="stroke:rgb(255,0,0);stroke-width:1" /> + <line x1="150" y1="35" x2="180" y2="35" style="stroke:rgb(255,0,0);stroke-width:1" /> +</svg> + +In fact, this algorithm always works. A brief explanation of correctness is as follows. If we have two events $E_1$ and $E_2$, with $E_2$ ending later than $E_1$, then it is always optimal to select $E_1$. This is because selecting $E_1$ gives us more choices for future events. If we can select an event to go after $E_2$, then that event can also go after $E_1$, because $E_1$ ends first. Thus, the set of events that can go after $E_2$ is a subset of the events that can go after $E_1$, making $E_1$ the optimal choice. + +For the following code, let's say we have the array `events` of events, which each contain a start and an end point. + +<LanguageSection> + +<CPPSection> + +We'll be using the C++ built in container pair to store each event. Note that since the standard sort in C++ sorts by first element, we will store each event as `pair<end, start>`. + +```cpp +// read in the input, store the events in pair<int, int>[] events. +sort(events, events + n); // sorts by first element (ending time) +int currentEventEnd = -1; // end of event currently attending +int ans = 0; // how many events were attended? +for(int i = 0; i < n; i++){ // process events in order of end time + if(events[i].second >= currentEventEnd){ // if event can be attended + // we know that this is the earliest ending event that we can attend + // because of how the events are sorted + currentEventEnd = events[i].first; + ans++; + } +} +cout << ans << endl; +``` + +</CPPSection> + +<JavaSection> + +We'll be using the following static class to store each event: + +```java +static class Event implements Comparable<Event>{ + int start; int end; + public Event(int s, int e){ + start = s; end = e; + } + public int compareTo(Event e){ + return Integer.compare(this.end, e.end); + } +} +``` + + +```java +// read in the input, store the events in Event[] events. +Arrays.sort(events); // sorts by comparator we defined above +int currentEventEnd = -1; // end of event currently attending +int ans = 0; // how many events were attended? +for(int i = 0; i < n; i++){ // process events in order of end time + if(events[i].start >= currentEventEnd){ // if event can be attended + // we know that this is the earliest ending event that we can attend + // because of how the events are sorted + currentEventEnd = events[i].end; + ans++; + } +} +pw.println(ans); +pw.close(); +``` + +</JavaSection> + +</LanguageSection> + + +## When Greedy Fails + +We'll provide a few common examples of when greedy fails, so that you can avoid falling into obvious traps and wasting time getting wrong answers in contest. + +### Coin Change + +This problem gives several coin denominations, and asks for the minimum number of coins needed to make a certain value. Greedy algorithms can be used to solve this problem only in very specific cases (it can be proven that it works for the American as well as the Euro coin systems). However, it doesn't work in the general case. For example, let the coin denominations be $\{1, 3, 4\}$, and say the value we want is 6. The optimal solution is $\{3, 3\}$, which requires only two coins, but the greedy method of taking the highest possible valued coin that fits in the remaining denomination gives the solution $\{4, 1, 1\}$, which is incorrect. + +### Knapsack + +The knapsack problem gives a number of items, each having a **weight** and a **value**, and we want to choose a subset of these items. We are limited to a certain weight, and we want to maximize the value of the items that we take. + +Let's take the following example, where we have a maximum capacity of 4: + +<center> + +| Item | Weight | Value | Value Per Weight | +|------|--------|-------|------------------| +| A | 3 | 18 | 6 | +| B | 2 | 10 | 5 | +| C | 2 | 10 | 5 | + +</center> + +If we use greedy based on highest value first, we choose item A and then we are done, as we don't have remaining weight to fit either of the other two. Using greedy based on value per weight again selects item A and then quits. However, the optimal solution is to select items B and C, as they combined have a higher value than item A alone. In fact, there is no working greedy solution. The solution to this problem uses **dynamic programming**, which is covered in gold. + + + +## CSES Problems + +<Problems problems={problems.cses} /> + +## Other Problems + +<Problems problems={problems.other} />
\ No newline at end of file diff --git a/content/3_Silver/Harder_Ordered.txt b/content/3_Silver/Harder_Ordered.txt new file mode 100644 index 0000000..e503ca2 --- /dev/null +++ b/content/3_Silver/Harder_Ordered.txt @@ -0,0 +1,15 @@ +--- +id: harder-ordered +title: "Harder Problems with Ordered Sets" +author: Benjamin Qi +prerequisites: + - custom-cpp-stl +description: "More advanced uses of ordered sets with example problems." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { +}; + diff --git a/content/3_Silver/Intro_Ordered.mdx b/content/3_Silver/Intro_Ordered.mdx new file mode 100644 index 0000000..638eb40 --- /dev/null +++ b/content/3_Silver/Intro_Ordered.mdx @@ -0,0 +1,391 @@ +--- +id: intro-ordered +title: "Introduction to Ordered Sets" +author: Darren Yao, Benjamin Qi +prerequisites: + - unordered +description: "By maintaining keys in sorted order." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + standard: [ + new Problem("CSES", "Concert Tickets", "1091", "Easy", false, ["iterators"], "just do upper_bound"), + new Problem("CSES", "Traffic Lights", "1163", "Normal", false, ["set"], "just insert into set one at a time"), + ], + sample: [ + new Problem("CSES", "Bit Inversions", "1188", "Normal", false, []), + ], + general: [ + new Problem("Silver", "Milk Measurement", "763", "Normal", false, []), + new Problem("Silver", "Convention II", "859", "Normal", false, []), + new Problem("Gold", "Snow Boots", "813", "Normal", false, []), + new Problem("CF", "Jury Marks", "contest/831/problem/C", "Hard", false, [], "Hard, would recommend skipping (1700 on CF)"), + new Problem("CF", "Mahmoud & Ehab & Function", "contest/862/problem/E", "Hard", false, [], "Hard, do not attempt until Gold/Plat (2100 on CF)"), + new Problem("CF", "Tournament", "contest/878/problem/C", "Very Hard", false, [], "First solve problem for $n$-th tournament only. Extremely hard, do not attempt (2700 on CF)"), + ] +}; + +<Warning> + +This is **not** built into Python. The Python [OrderedDict](https://docs.python.org/3/library/collections.html#collections.OrderedDict) stores keys in the same order as they were inserted in, **not** in sorted order. + +</Warning> + +<Resources> + <Resource source="IUSACO" title="4.3 - Sets & Maps">module is based off this</Resource> + <Resource source="CPH" title="4.2 to 4.4 - Data Structures" starred>sets, maps, set iterators</Resource> +</Resources> + +<br /> + +In **ordered** sets and maps, the entries are sorted in order of key. Insertions, deletions, and searches are all $O(\log N)$, where $N$ is the number of elements in the set or map. Accessing or removing the next key higher or lower than some input $k$ is also supported. + +## Ordered Sets + +<LanguageSection> + +<CPPSection> + +In addition to the `unordered_set` operations mentioned in the prerequisite module, the ordered `set` also allows: + + - `lower_bound`: returns an iterator to the least element greater than or equal to some element `k` + - `upper_bound`: returns an iterator to the least element strictly greater than some element `k`. + +```cpp +set<int> s; +s.insert(1); // [1] +s.insert(14); // [1, 14] +s.insert(9); // [1, 9, 14] +s.insert(2); // [1, 2, 9, 14] +cout << *s.upper_bound(7) << '\n'; // 9 +cout << *s.upper_bound(9) << '\n'; // 14 +cout << *s.lower_bound(5) << '\n'; // 9 +cout << *s.lower_bound(9) << '\n'; // 9 +cout << *s.begin() << '\n'; // 1 +auto it = s.end(); +cout << *(--it) << '\n'; // 14 +s.erase(s.upper_bound(6)); // [1, 2, 14] +``` + +</CPPSection> + +<JavaSection> + +As well as those supported by the unordered set, the ordered set also allows four additional operations: `first`, which returns the lowest element in the set, `last`, which returns the highest element in the set, `lower`, which returns the greatest element strictly less than some element, and `higher`, which returns the least element strictly greater than it. + +```java +TreeSet<Integer> set = new TreeSet<Integer>(); +set.add(1); // [1] +set.add(14); // [1, 14] +set.add(9); // [1, 9, 14] +set.add(2); // [1, 2, 9, 14] +System.out.println(set.higher(7)); // 9 +System.out.println(set.higher(9)); // 14 +System.out.println(set.lower(5)); // 2 +System.out.println(set.first()); // 1 +System.out.println(set.last()); // 14 +set.remove(set.higher(6)); // [1, 2, 14] +System.out.println(set.higher(23); // ERROR, no such element exists +``` + +</JavaSection> + +</LanguageSection> + +One limitation of the ordered set is that we can't efficiently access the $k^{th}$ largest element in the set, or find the number of elements in the set greater than some arbitrary $x$. These operations can be handled using a data structure called an [order statistic tree](../gold/PURS#order-statistic-tree). + +## Ordered Maps + +<LanguageSection> + +<CPPSection> + +In addition to the `unordered_map` operations mentioned in the prerequisite module, the ordered `map` also allows: + + - `lower_bound`: returns the iterator pointing to the lowest entry not less than the specified key + - `upper_bound`: returns the iterator pointing to the lowest entry strictly greater than the specified key respectively. + +```cpp +map<int, int> m; +m[3] = 5; // [(3, 5)] +m[11] = 4; // [(3, 5); (11, 4)] +m[10] = 491; // [(3, 5); (10, 491); (11, 4)] +cout << m.lower_bound(10)->first << " " << m.lower_bound(10)->second << '\n'; // 10 491 +cout << m.upper_bound(10)->first << " " << m.upper_bound(10)->second << '\n'; // 11 4 +m.erase(11); // [(3, 5); (10, 491)] +if (m.upper_bound(10) == m.end()) +{ + cout << "end" << endl; // Prints end +} +``` + +</CPPSection> + +<JavaSection> + +The ordered map supports all of the operations that an unordered map supports, and additionally supports `firstKey` / `firstEntry` and `lastKey` / `lastEntry`, returning the lowest key/entry and the highest key/entry, as well as `higherKey` / `higherEntry` and `lowerKey` / `lowerEntry`, returning the lowest key/entry strictly higher than the specified key, or the highest key/entry strictly lower than the specified key. + +```java +TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); +map.put(3, 5); // [(3, 5)] +map.put(11, 4); // [(3, 5); (11, 4)] +map.put(10, 491); // [(3, 5); (10, 491); (11, 4)] +System.out.println(map.firstKey()); // 3 +System.out.println(map.firstEntry()); // (3, 5) +System.out.println(map.lastEntry()); // (11, 4) +System.out.println(map.higherEntry(4)); // (10, 491) +map.remove(11); // [(3, 5); (10, 491)] +System.out.println(map.lowerKey(4)); // 3 +System.out.println(map.lowerKey(3)); // ERROR +``` + +</JavaSection> + +</LanguageSection> + +## Multisets + +A **multiset** is a sorted set that allows multiple copies of the same element. + +<LanguageSection> + +<CPPSection> + +In addition to all of the regular set operations, + + - the `count()` method returns the number of times an element is present in the multiset. However, this method takes time **linear** in the number of matches so you shouldn't use it in a contest. + - If you want to remove a value *once*, make sure to use `multiset.erase(multiset.find(val))` rather than `multiset.erase(val)`. The latter will remove *all* instances of `val`. + + +```cpp +multiset<int> ms; +ms.insert(1); // [1] +ms.insert(14); // [1, 14] +ms.insert(9); // [1, 9, 14] +ms.insert(2); // [1, 2, 9, 14] +ms.insert(9); // [1, 2, 9, 9, 14] +ms.insert(9); // [1, 2, 9, 9, 9, 14] +cout << ms.count(4) << '\n'; // 0 +cout << ms.count(9) << '\n'; // 3 +cout << ms.count(14) << '\n'; // 1 +ms.erase(ms.find(9)); +cout << ms.count(9) << '\n'; // 2 +ms.erase(9); +cout << ms.count(9) << '\n'; // 0 +``` + +</CPPSection> + +<JavaSection> + +While there is no multiset in Java, we can implement one using the `TreeMap` from values to their respective frequencies. We declare the `TreeMap` implementation globally so that we can write functions for adding and removing elements from it. The `first`, `last`, `higher`, and `lower` operations still function as intended; just use `firstKey`, `lastKey`, `higherKey`, and `lowerKey` respectively. + + +```java +static TreeMap<Integer, Integer> multiset = new TreeMap<Integer, Integer>(); + +public static void main(String[] args){ + ... +} + +static void add(int x){ + if(multiset.containsKey(x)){ + multiset.put(x, multiset.get(x) + 1); + } else { + multiset.put(x, 1); + } +} + +static void remove(int x){ + multiset.put(x, multiset.get(x) - 1); + if(multiset.get(x) == 0){ + multiset.remove(x); + } +} +``` + +</JavaSection> + +</LanguageSection> + +## Using Iterators + +<LanguageSection> + +<CPPSection> + +next(), prev(), ++, -- + + +</CPPSection> + +</LanguageSection> + +<IncompleteSection /> + +## Priority Queues + +A **priority queue** supports the following operations: insertion of elements, deletion of the element considered highest priority, and retrieval of the highest priority element, all in $O(\log N)$ time according to the number of elements in the priority queue. Like sets, priority is based on a **comparator function**. + +Of course, all of these functions are supported by multisets, but priority queues are simpler and faster. + +<LanguageSection> + +<CPPSection> + +### [C++](http://www.cplusplus.com/reference/queue/priority_queue/) + +```cpp +priority_queue<int> pq; +pq.push(7); // [7] +pq.push(2); // [2, 7] +pq.push(1); // [1, 2, 7] +pq.push(5); // [1, 2, 5, 7] +cout << pq.top() << endl; // 7 +pq.pop(); // [1, 2, 5] +pq.pop(); // [1, 2] +pq.push(6); // [1, 2, 6] +``` + +</CPPSection> + +<JavaSection> + +### Java + +In Java, we delete and retrieve the element of **lowest** rather than highest priority. + +```java +PriorityQueue<Integer> pq = new PriorityQueue<Integer>(); +pq.add(7); // [7] +pq.add(2); // [7, 2] +pq.add(1); // [7, 2, 1] +pq.add(5); // [7, 5, 2, 1] +System.out.println(pq.peek()); // 1 +pq.poll(); // [7, 5, 2] +pq.poll(); // [7, 5] +pq.add(6); // [7, 6, 5] +``` + +</JavaSection> + +</LanguageSection> + +## Introductory Problems + +<Problems problems={problems.standard} /> + +## Harder Example: Bit Inversions + +<Problems problems={problems.sample} /> + +### Solution + +<Spoiler title="Solution"> + +<LanguageSection> + +<CPPSection> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +#define sz(x) (x).size() + +string s; +int m; +multiset<int> ans, ret; + +void modify(int x) { + if (x == 0 || x == sz(s)) return; + auto it = ans.find(x); + if (it != end(ans)) { + int a = *prev(it), b = *next(it); + ret.erase(ret.find(x-a)), ret.erase(ret.find(b-x)); + ret.insert(b-a); + ans.erase(it); + } else { + it = ans.insert(x); + int a = *prev(it), b = *next(it); + ret.erase(ret.find(b-a)); + ret.insert(x-a), ret.insert(b-x); + } +} + +int main() { + ios_base::sync_with_stdio(0); cin.tie(0); + cin >> s >> m; + ans.insert(0); ans.insert(sz(s)); + for (int i = 0; i < sz(s)-1; ++i) + if (s[i] != s[i+1]) ans.insert(i+1); + for (auto it = ans.begin(); next(it) != ans.end(); it ++) + ret.insert(*next(it)-*it); + for (int i = 0; i < m; ++i) { + int x; cin >> x; + modify(x-1); modify(x); + cout << *ret.rbegin() << " "; + } +} +``` + +<br /> + +Note that `multiset` has a high constant factor, so replacing `ret` with an array and a `priority_queue` reduces the runtime by a factor of 2. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +#define sz(x) (int)(x).size() + +string s; +int m; +set<int> ans; +priority_queue<int> ret; +int cnt[200005]; + +void ad(int x) { cnt[x] ++; ret.push(x); } +void modify(int x) { + if (x == 0 || x == sz(s)) return; + auto it = ans.find(x); + if (it != end(ans)) { + int a = *prev(it), b = *next(it); ans.erase(it); + cnt[x-a] --, cnt[b-x] --; ad(b-a); + } else { + it = ans.insert(x).first; + int a = *prev(it), b = *next(it); + cnt[b-a] --, ad(x-a), ad(b-x); + } +} + +int main() { + ios_base::sync_with_stdio(0); cin.tie(0); + cin >> s >> m; + ans.insert(0); ans.insert(sz(s)); + for (int i = 0; i < sz(s)-1; ++i) + if (s[i] != s[i+1]) ans.insert(i+1); + for (auto it = ans.begin(); next(it) != ans.end(); it ++) + ad(*next(it)-*it); + for (int i = 0; i < m; ++i) { + int x; cin >> x; + modify(x-1); modify(x); + while (!cnt[ret.top()]) ret.pop(); + cout << ret.top() << " "; + } +} +``` + + +</CPPSection> + +</LanguageSection> + +</Spoiler> + +## Harder Problems + +<Problems problems={problems.general} /> diff --git a/content/3_Silver/Prefix_Sums.mdx b/content/3_Silver/Prefix_Sums.mdx new file mode 100644 index 0000000..feb8a9b --- /dev/null +++ b/content/3_Silver/Prefix_Sums.mdx @@ -0,0 +1,509 @@ +--- +id: prefix-sums +title: "Prefix Sums" +author: Darren Yao, Eric Wei +description: "Computing range sum queries in constant time over a fixed array." +prerequisites: + - intro-ds +frequency: 3 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("YS", "Static Range Sum", "static_range_sum", "Easy", false, [], "equivalent to [CSES Range Sum Queries I](https://cses.fi/problemset/task/1646)"), + ], + cum: [ + new Problem("LC", "Find Pivot Index", "find-pivot-index", "Easy", false, [], "Calculate all prefix and suffix sums"), + new Problem("Silver", "Breed Counting", "572", "Easy", false, [], "Find separate prefix sums for each breed"), + new Problem("Silver", "Subsequences Summing to Sevens", "595", "Easy", false, [], "Find prefix sums modulo 7, and find a pair of equal (modulo 7) prefix sums as far away from each other as possible"), + new Problem("Silver", "Hoof Paper Scissors", "691", "Easy", false, [], "Find prefix sums for the number of hoofs, papers, and scissors separately, the enumerate where the change takes place and consider all the possibilities of what gesture to use before and after the change"), + new Problem("Silver", "Max Cross", "715", "Easy", false, [], "Find prefix sum of number of broken signals, then enumerate all possible locations of the interval"), + 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"), + ], + maxsumext: [ + new Problem("CSES", "Max Subarray Sum II", "1644", "Normal", false, ["Prefix Sums"], "Enumerate the right endpoint while maintaining multiset of prefix sums in the interval where the left endpoint might lie in"), + ], + sample2: [ + new Problem("CSES", "Forest Queries", "1652", "Easy", false, ["Prefix Sums"], ""), + ], + cum2: [ + new Problem("Silver", "Painting the Barn", "919", "Easy", false, ["Prefix Sums"], "When adding a rectangle, we need to add one to some corners and subtract one to other corners (think about which) in a temporary 2D array. At the end, take its prefix sum."), + new Problem("Gold", "Painting the Barn", "923", "Hard", false, ["Prefix Sums", "Max Subarray Sum"], "Treat areas that currently have K-1 layers of paint as having a value of 1, those having K layers of paint as having a value of -1, and the rest as having a value of 0. For each pair of x-coordinates, find the maximum subrectangle whose left side is located at the first x-coordinate and the right side is located at the second x-coordinate. This can be done with maximum subarray sum and prefix sums of each row. Do something similar with each pair of y-coordinates (fixing the top and bottom of the rectangle). Also notice that whenever two disjoint rectangles are drawn, there always exists a horizontal or vertical line separating them. Enumerate the location of the line, and find maximum subrectangle of each side using the information that we precalculated."), + ], + related: [ + new Problem("Silver", "My Cow Ate My Homework", "762", "Easy", false, ["Prefix Sums"], "Enumerate, from right to left, the last question eaten. The problem is reduced to suffix minimums."), + new Problem("CSES", "Range XOR Queries", "1650", "Easy", false, ["Prefix Sums"], "XOR prefix[r] with prefix[l-1]"), + ], + complex: [ + new Problem("Google KickStart", "Candies (Test Set 1)", "https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff43/0000000000337b4d", "Easy", false, ["Prefix Sums"], ""), + new Problem("AC", "Multiple of 2019", "https://atcoder.jp/contests/abc164/tasks/abc164_d", "Hard", false, ["Prefix Sums"], "Make use of the fact that adding 0's to the end of a number does not affect whether it is a multiple of 2019 (because 10 and 2019 are coprime)."), + ], +}; + + +<Resources title="Additional"> + <Resource source="IUSACO" title="11 - Prefix Sums" starred>module is based off this</Resource> + <Resource source="CPH" title="9.1 - Sum Queries">rather brief</Resource> + <Resource source="PAPS" title="11.2.1 - Prefix Precomputation">also rather brief</Resource> +</Resources> + +## Introduction + +<Problems problems={problems.sample} /> + +Let's say we have a one-indexed integer array $\texttt{arr}$ of size $N$ and we want to compute the value of +$$ +\texttt{arr}[a]+\texttt{arr}[a+1]+\cdots+\texttt{arr}[b] +$$ +for $Q$ different pairs $(a,b)$ satisfying $1\le a\le b\le N$. We'll use the following example with $N = 6$: + +<center> + +| Index $i$ | 1 | 2 | 3 | 4 | 5 | 6 | +| --- | --- | --- | --- | --- | --- | --- | +| $\texttt{arr}[i]$ | 1 | 6 | 4 | 2 | 5 | 3 | + +</center> + +Naively, for every query, we can iterate through all entries from index $a$ to index $b$ to add them up. Since we have $Q$ queries and each query requires a maximum of $O(N)$ operations to calculate the sum, our total time complexity is $O(NQ)$. For most problems of this nature, the constraints will be $N, Q \leq 10^5$, so $NQ$ is on the order of $10^{10}$. This is **not** acceptable; it will almost certainly exceed the time limit. + +Instead, we can use prefix sums to process these array sum queries. We designate a prefix sum array $\texttt{prefix}$. First, because we're 1-indexing the array, set $\texttt{prefix}[0]=0$, then for indices $k$ such that $1 \leq k \leq n$, define the prefix sum array as follows: + +$$ +\texttt{prefix}[k]=\sum_{i=1}^{k} \texttt{arr}[i] +$$ + +Basically, what this means is that the element at index $k$ of the prefix sum array stores the sum of all the elements in the original array from index $1$ up to $k$. This can be calculated easily in $O(N)$ by the following formula for each $1\le k\le n$: + +$$ +\texttt{prefix}[k]=\texttt{prefix}[k-1]+\texttt{arr}[k] +$$ + +For the example case, our prefix sum array looks like this: + +<center> + +| Index $i$ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| $\texttt{prefix}[i]$ | 0 | 1 | 7 | 11 | 13 | 18 | 21 | + +</center> + +Now, when we want to query for the sum of the elements of $\texttt{arr}$ between (1-indexed) indices $a$ and $b$ inclusive, we can use the following formula: + +$$ +\sum_{i=L}^{R} \texttt{arr}[i] = \sum_{i=1}^{R} \texttt{arr}[i] - \sum_{i=1}^{L-1} \texttt{arr}[i] +$$ + +Using our definition of the elements in the prefix sum array, we have + +$$ +\sum_{i=L}^{R} \texttt{arr}[i]= \texttt{prefix}[R]-\texttt{prefix}[L-1] +$$ + +Since we are only querying two elements in the prefix sum array, we can calculate subarray sums in $O(1)$ per query, which is much better than the $O(N)$ per query that we had before. Now, after an $O(N)$ preprocessing to calculate the prefix sum array, each of the $Q$ queries takes $O(1)$ time. Thus, our total time complexity is $O(N+Q)$, which should now pass the time limit. + +Let's do an example query and find the subarray sum between indices $a = 2$ and $b = 5$, inclusive, in the 1-indexed $\texttt{arr}$. From looking at the original array, we see that this is + +$$ +\sum_{i=2}^{5} \texttt{arr}[i] = 6 + 4 + 2 + 5 = 17. +$$ + +<center> + +<table> +<thead> +<tr> +<th> + +<!-- extremely dumb formatting should be fixed by better mdx parser in mdx 2.0 --> + +Index $ijkl$ + +</th> +<th>1</th> +<th>2</th> +<th>3</th> +<th>4</th> +<th>5</th> +<th>6</th> +</tr> +</thead> +<tbody> +<tr> +<td> + +$\texttt{arr}[i]$ + +</td> +<td>1</td> +<td className="bg-yellow-100">6</td> +<td className="bg-yellow-100">4</td> +<td className="bg-yellow-100">2</td> +<td className="bg-yellow-100">5</td> +<td>3</td> +</tr> +</tbody> +</table> + +</center> + +Using prefix sums: +$$ +\texttt{prefix}[5] - \texttt{prefix}[1] = 18 - 1 = 17. +$$ + +<center> + +<table> +<thead> +<tr> +<th> + +Index $i$ + +</th> +<th>0</th> +<th>1</th> +<th>2</th> +<th>3</th> +<th>4</th> +<th>5</th> +<th>6</th> +</tr> +</thead> +<tbody> +<tr> +<td> + +$\texttt{prefix}[i]$ + +</td> +<td className="bg-red-100">0</td> +<td className="bg-red-100">1</td> +<td className="bg-green-100">7</td> +<td className="bg-green-100">11</td> +<td className="bg-green-100">13</td> +<td className="bg-green-100">18</td> +<td>21</td> +</tr> +</tbody> +</table> + +</center> + +These are also known as [partial sums](https://mathworld.wolfram.com/PartialSum.html). + +### Problems + +<Problems problems={problems.cum} /> + +## Max Subarray Sum + +Now we'll look at some extensions. + +<Problems problems={problems.maxsum} /> + +This problem has a solution known as [Kadane's Algorithm](https://en.wikipedia.org/wiki/Maximum_subarray_problem#Kadane's_algorithm). Please don't use that solution; try to solve it with prefix sums. + +<Spoiler title="Why are the two methods equivalent?"> + +Consider the desired maximum subarray. As you go along from left to right, the prefix sum solution will mark the start of that subarray as the "current minimum prefix sum". Kadane's Algorithm, on the other hand, will set the current value to 0 at that point. As both solutions iterate through the array, they eventually find the right side of the maximum sum, and they find the answer to the problem at that location. In essence, Kadane's Algorithm stores the maximum sum of a subarray that ends at the current location (which is what the prefix sum solution calculates on each iteration), but it calculates this value greedily instead. + +</Spoiler> + +## Prefix Minimum, XOR, etc. + +Similar to prefix sums, you can also take prefix minimum or maximum; but *you cannot* answer min queries over an arbitrary range with prefix minimum. (This is because minimum doesn't have an inverse operation, the way subtraction is to addition.) On the other hand, XOR is its own inverse operation, meaning that the XOR of any number with itself is zero. + +<Problems problems={problems.related} /> + +## 2D Prefix Sums + +<Problems problems={problems.sample2} /> + +Now, what if we wanted to process $Q$ queries for the sum over a subrectangle of a $N$ rows by $M$ columns matrix in two dimensions? Let's assume both rows and columns are 1-indexed, and we use the following matrix as an example: + +<center> + +<table className="text-center"> +<tbody> + <tr> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + </tr> + <tr> + <td>0</td> + <td>1</td> + <td>5</td> + <td>6</td> + <td>11</td> + <td>8</td> + </tr> + <tr> + <td>0</td> + <td>1</td> + <td>7</td> + <td>11</td> + <td>9</td> + <td>4</td> + </tr> + <tr> + <td>0</td> + <td>4</td> + <td>6</td> + <td>1</td> + <td>3</td> + <td>2</td> + </tr> + <tr> + <td>0</td> + <td>7</td> + <td>5</td> + <td>4</td> + <td>2</td> + <td>3</td> + </tr> +</tbody> +</table> + +</center> + +Naively, each sum query would then take $O(NM)$ time, for a total of $O(QNM)$. This is too slow. + +Let's take the following example region, which we want to sum: + + +<center> + +<table className="text-center"> +<tbody> + <tr> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + </tr> + <tr> + <td>0</td> + <td>1</td> + <td>5</td> + <td>6</td> + <td>11</td> + <td>8</td> + </tr> + <tr> + <td>0</td> + <td>1</td> + <td className="bg-blue-100">7</td> + <td className="bg-blue-100">11</td> + <td className="bg-blue-100">9</td> + <td>4</td> + </tr> + <tr> + <td>0</td> + <td>4</td> + <td className="bg-blue-100">6</td> + <td className="bg-blue-100">1</td> + <td className="bg-blue-100">3</td> + <td>2</td> + </tr> + <tr> + <td>0</td> + <td>7</td> + <td>5</td> + <td>4</td> + <td>2</td> + <td>3</td> + </tr> +</tbody> +</table> + +</center> + +Manually summing all the cells, we have a submatrix sum of $7+11+9+6+1+3 = 37$. + +The first logical optimization would be to do one-dimensional prefix sums of each row. Then, we'd have the following row-prefix sum matrix. The desired subarray sum of each row in our desired region is simply the green cell minus the red cell in that respective row. We do this for each row to get $(28-1) + (14-4) = 37$. + +<center> + +<table className="text-center"> +<tbody> +<tr> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> +</tr> +<tr> + <td>0</td> + <td>1</td> + <td>6</td> + <td>12</td> + <td>23</td> + <td>31</td> +</tr> +<tr> + <td>0</td> + <td className="bg-red-200">1</td> + <td>8</td> + <td>19</td> + <td className="bg-green-200">28</td> + <td>32</td> +</tr> +<tr> + <td>0</td> + <td className="bg-red-200">4</td> + <td>10</td> + <td>11</td> + <td className="bg-green-200">14</td> + <td>16</td> +</tr> +<tr> + <td>0</td> + <td>7</td> + <td>12</td> + <td>16</td> + <td>18</td> + <td>21</td> +</tr> +</tbody> +</table> + +</center> + +Now, if we wanted to find a submatrix sum, we could break up the submatrix into a subarray for each row, and then add their sums, which would be calculated using the prefix sums method described earlier. Since the matrix has $N$ rows, the time complexity of this is $O(QN)$. This might be fast enough for $Q=10^5$ and $N=10^3$, but we can do better. + +In fact, we can do two-dimensional prefix sums. In our two dimensional prefix sum array, we have + +$$ +\texttt{prefix}[a][b]=\sum_{i=1}^{a} \sum_{j=1}^{b} \texttt{arr}[i][j]. +$$ + +This can be calculated as follows for row index $1 \leq i \leq n$ and column index $1 \leq j \leq m$: +$$ +\texttt{prefix}[i][j] + = \texttt{prefix}[i-1][j] + + \texttt{prefix}[i][j-1] + - \texttt{prefix}[i-1][j-1] + + \texttt{arr}[i][j] +$$ + +The submatrix sum between rows $a$ and $A$ and columns $b$ and $B$, can thus be expressed as follows: + +$$ +\sum_{i=a}^{A} \sum_{j=b}^{B} \texttt{arr}[i][j]=\texttt{prefix}[A][B] + - \texttt{prefix}[a-1][B] + - \texttt{prefix}[A][b-1] + + \texttt{prefix}[a-1][b-1] +$$ + +Summing the blue region from above using the 2D prefix sums method, we add the value of the green square, subtract the values of the red squares, and then add the value of the gray square. In this example, we have + +$$ +65-23-6+1 = 37, +$$ + +as expected. + +<center> + +<table className="text-center"> +<tbody> + <tr> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + <td>0</td> + </tr> + <tr> + <td>0</td> + <td className="bg-gray-200">1</td> + <td>6</td> + <td>12</td> + <td className="bg-red-200">23</td> + <td>31</td> + </tr> + <tr> + <td>0</td> + <td>2</td> + <td>14</td> + <td>31</td> + <td>51</td> + <td>63</td> + </tr> + <tr> + <td>0</td> + <td className="bg-red-200">6</td> + <td>24</td> + <td>42</td> + <td className="bg-green-200">65</td> + <td>79</td> + </tr> + <tr> + <td>0</td> + <td>13</td> + <td>36</td> + <td>58</td> + <td>83</td> + <td>100</td> + </tr> +</tbody> +</table> + +</center> + +Since no matter the size of the submatrix we are summing, we only need to access four values of the 2D prefix sum array, this runs in $O(1)$ per query after an $O(NM)$ preprocessing. + +<Problems problems={problems.cum2} /> + +## More Complex Applications + +Instead of storing just the values themselves, you can also take a prefix sum over $i\cdot a_i$, or $10^i \cdot a_i$, for instance. Some math is usually helpful for these problems; don't be afraid to get dirty with algebra! + +For instance, let's see how to quickly answer the following type of query: + +> Find $1\cdot a_l+2\cdot a_{l+1}+3\cdot a_{l+2}+\cdots+(r-l+1)\cdot a_{r}$. + +First, define the following: +$$ +\texttt{ps}[i] = a_1+a_2+a_3+a_4+\cdots+a_i +$$ +$$ +\texttt{ips}[i] = 1\cdot a_1+2\cdot a_2+\cdots+i\cdot a_i +$$ + +Then, we have: +$$ +l\cdot a_l + (l+1) \cdot a_{l+1} + \cdots + r \cdot a_r = \texttt{ips}[r]-\texttt{ips}[l-1] +$$ +$$ +(l-1) \cdot a_l + (l-1) \cdot a_{l+1} + \cdot + (l-1) \cdot a_r = (l-1)(\texttt{ps}[r]-\texttt{ps}[l-1]) +$$ + +And so, +$$ +1\cdot a_l + 2 \cdot a_{l+1} + \cdots + (r-l+1) \cdot a_r = \texttt{ips}[r]-\texttt{ips}[l-1]-(l-1)(\texttt{ps}[r]-\texttt{ps}[l-1]) +$$ + +Which is what we were looking for! + +<Problems problems={problems.complex} /> diff --git a/content/3_Silver/Sorting_2_Old.txt b/content/3_Silver/Sorting_2_Old.txt new file mode 100644 index 0000000..e8df3d0 --- /dev/null +++ b/content/3_Silver/Sorting_2_Old.txt @@ -0,0 +1,217 @@ +--- +id: sorting-custom-2 +title: "Sorting with Custom Comparators Pt 2" +author: Darren Yao, Siyong Huang, Michael Cao, Benjamin Qi +prerequisites: + - Bronze - Pairs & Tuples in C++ + - Bronze - Introduction to Data Structures +description: "Both Java and C++ have built-in functions for sorting. However, if we use custom objects, or if we want to sort elements in a different order, then we'll need to use a custom comparator. " +--- + +## Sorting by Multiple Criteria + +Now, suppose we wanted to sort a list of `Person`s in ascending order, primarily by height and secondarily by weight. We can do this quite similarly to how we handled sorting by one criterion earlier. What the comparator function needs to do is to compare the weights if the heights are equal, and otherwise compare heights, as that's the primary sorting criterion. + +### C++ + + +```cpp +bool cmp(Person a, Person b) { + if(a.height == b.height) { + return a.weight < b.weight; + } + return a.height < b.height; +} + +int main() { + sort(arr, arr+N, cmp); // sorts the array in ascending order by height and then weight if the heights are equal +} +``` + +Sorting with an arbitrary number of criteria is done similarly. + +### Java + +```java +static class Person implements Comparable<Person>{ + int height, weight; + public Person(int h, int w){ + height = h; weight = w; + } + public int compareTo(Person p){ + if(height == p.height){ + return Integer.compare(weight, p.weight); + } else { + return Integer.compare(height, p.height); + } + } +} +``` + +Sorting with an arbitrary number of criteria is done similarly. + +An alternative way of representing custom objects is with arrays. Instead of using a custom object to store data about each person, we can simply use `int[]`, where each `int` array is of size 2, and stores pairs of {height, weight}, probably in the form of a list like `ArrayList<int[]>`. Since arrays aren't objects in the usual sense, we need to use `Comparator`. Example for sorting by the same two criteria as above: + +```java +static class Comp implements Comparator<int[]>{ + public int compare(int[] a, int[] b){ + if(a[0] == b[0]){ + return Integer.compare(a[1], b[1]); + } else { + return Integer.compare(a[0], b[0]); + } + } +} +``` + +I don't recommend using arrays to represent objects mostly because it's confusing, but it's worth noting that some competitors do this. + +### Python + +Operator Overloading can be used + +<!-- Tested --> +```py +import random +class Foo: + def __init__(self, _Bar, _Baz): self.Bar,self.Baz = _Bar,_Baz + def __str__(self): return "Foo({},{})".format(self.Bar, self.Baz) + def __lt__(self, o): # sort by increasing Bar, breaking ties by decreasing Baz + if self.Bar != o.Bar: return self.Bar < o.Bar + if self.Baz != o.Baz: return self.Baz > o.Baz + return False + +a = [] +for i in range(8): + a.append(Foo(random.randint(1, 9), random.randint(1, 9))) +print(*a) +print(*sorted(a)) +``` + +Output: + +``` +Foo(1,2) Foo(3,2) Foo(6,6) Foo(9,7) Foo(8,7) Foo(8,9) Foo(6,9) Foo(9,8) +Foo(1,2) Foo(3,2) Foo(6,9) Foo(6,6) Foo(8,9) Foo(8,7) Foo(9,8) Foo(9,7) +``` + +A custom comparator works as well + - Lambdas aren't shown here because they are typically used as one-liners + +```py +import random +from functools import cmp_to_key + +class Foo: + def __init__(self, _Bar, _Baz): self.Bar,self.Baz = _Bar,_Baz + def __str__(self): return "Foo({},{})".format(self.Bar, self.Baz) + +a = [] +for i in range(8): + a.append(Foo(random.randint(1, 9), random.randint(1, 9))) +print(*a) + +def cmp(foo1, foo2): # Increasing Bar, breaking ties with decreasing Baz + if foo1.Bar != foo2.Bar: return -1 if foo1.Bar < foo2.Bar else 1 + if foo1.Baz != foo2.Baz: return -1 if foo1.Baz > foo2.Baz else 1 + return 0 +print(*sorted(a, key=cmp_to_key(cmp))) + +# Python automatically sorts tuples in increasing order with priority to the leftmost element +# You can sort objects by its mapping to a tuple of its elements +# The following sorts Foo by increasing Bar values, breaking ties with increasing Baz value +print(*sorted(a, key=lambda foo: (foo.Bar, foo.Baz))) +``` +Output: +``` +Foo(1,2) Foo(3,2) Foo(6,6) Foo(9,7) Foo(8,7) Foo(8,9) Foo(6,9) Foo(9,8) +Foo(1,2) Foo(3,2) Foo(6,9) Foo(6,6) Foo(8,9) Foo(8,7) Foo(9,8) Foo(9,7) +Foo(1,2) Foo(3,2) Foo(6,6) Foo(6,9) Foo(8,7) Foo(8,9) Foo(9,7) Foo(9,8) +``` + +## Comparators for C++ STL + +Operator overloading works as expected for using in STL. If you are sorting elements in reverse order, you can use the STL [greater](https://en.cppreference.com/w/cpp/utility/functional/greater) comparator instead. + +For function comparators, some extra care needs to be taken: + +<!-- Reasonably Tested --> +```cpp +struct foo +{ + //members +}; +auto cmp1 = [](const foo& a, const foo& b){return /*comparator function*/;}; +//This way is shorter to write, but don't forget to pass the comparator as a parameter in the constructor! +//If you need to create an array of the objects, I would either use pointers of the method shown below +set<foo, decltype(cmp1)> Set1(cmp1); +priority_queue<foo, vector<foo>, decltype(cmp1)> pq1(cmp1); +//Side Note: priority queue is sorted in REVERSE order (largest elements are first) +map<foo, bar, decltype(cmp1)> Map1(cmp1); + + +struct cmp2 +{ + bool operator () (const foo& a, const foo& b){return /*comparator function*/;} +}; +//Here you do not need to pass the comparator as a parameter +//This makes it easier to create arrays of stl objects +set<foo, cmp2> Set2; +priority_queue<foo, vector<foo>, cmp2> pq2; +map<foo, bar, cmp2> Map2; +set<foo, cmp2> Set2b[100];//array of sets with the comparator +``` + +### Example of Comparators for Primitives + +Since you cannot overload operators for primitives, you must use custom comparators. + +<!-- Tested --> +```cpp +#include <bits/stdc++.h> +using namespace std; + +const int N = 8; +int a[N], b[N] = {4,8,2,3,4,1,2,4}; +int main() +{ + printf("--- Comparator 1 ---\n"); + iota(a, a+N, 0); + sort(a, a+N, greater<int>()); + //sort a in decreasing order + for(int i=0;i<N;++i) printf("a[%d] = %d\n", i, a[i]); + printf("--- Comparator 2 ---\n"); + iota(a, a+N, 0); + sort(a, a+N, [](int x, int y){return b[x]<b[y]||(b[x]==b[y]&&x>y);}); + //sort a by increasing values of b[i], breaking ties by decreasing index + for(int i=0;i<N;++i) printf("a[%d] = %d: b[%d] = %d\n", i, a[i], a[i], b[a[i]]); +} + +/* +Output: +--- Comparator 1 --- +a[0] = 7 +a[1] = 6 +a[2] = 5 +a[3] = 4 +a[4] = 3 +a[5] = 2 +a[6] = 1 +a[7] = 0 +--- Comparator 2 --- +a[0] = 5: b[5] = 1 +a[1] = 6: b[6] = 2 +a[2] = 2: b[2] = 2 +a[3] = 3: b[3] = 3 +a[4] = 7: b[7] = 4 +a[5] = 4: b[4] = 4 +a[6] = 0: b[0] = 4 +a[7] = 1: b[1] = 8 +*/ +``` + +Comments: Comparator 1 sorts array $a$ in decreasing order. Comparator 2 sorts array $a$ in increasing $b[a[i]]$ value, breaking ties by placing the greater index first. + +## Problems + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/3_Silver/Sorting_Custom.mdx b/content/3_Silver/Sorting_Custom.mdx new file mode 100644 index 0000000..dd43eb3 --- /dev/null +++ b/content/3_Silver/Sorting_Custom.mdx @@ -0,0 +1,584 @@ +--- +id: sorting-custom +title: "Sorting with Custom Comparators" +frequency: 3 +author: Darren Yao, Siyong Huang, Michael Cao, Benjamin Qi +prerequisites: + - pairs-tuples + - lambda +description: "If we use custom objects or if we want to sort elements in an order other than the default, then we'll need to define a custom comparator." +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("Silver", "Wormhole Sort", "992", "Normal", false, [], ""), + ], + general: [ + new Problem("CSES", "Restaurant Customers", "1619", "Easy", false, [], "Just sort endpoints of intervals and process in order."), + new Problem("Silver", "Lifeguards", "786", "Easy", false, [], "Similar to above."), + new Problem("Silver", "Rental Service", "787", "Easy", false, [], ""), + new Problem("Silver", "Mountains", "896", "Easy", false, [], ""), + new Problem("Gold", "Splitting the Field", "645", "Normal", false, [], ""), + new Problem("Silver", "Triangles", "1015", "Hard", false, [], ""), + new Problem("Silver", "Meetings", "967", "Very Hard", false, [], ""), + ], +}; + +<Resources> + <Resource source="IUSACO" title="8 - Sorting & Comparators">partially based off this</Resource> + <Resource source="CPH" title="3.2 - User-Defined Structs, Comparison Functions">short overflow of what this module will cover</Resource> +</Resources> + +<br /> + +## Example: Wormhole Sort + +<Problems problems={problems.sample} /> + +There are multiple ways to solve this problem. We won't discuss the full solution here, but all of them start by sorting the edges in nondecreasing order of weight. For example, the sample contains the following edges: + +``` +1 2 9 +1 3 7 +2 3 10 +2 4 3 +``` + +After sorting, it should look like + +``` +2 4 3 +1 3 7 +1 2 9 +2 3 10 +``` + +With C++, the easiest method is to use a `vector` of nested `pair`s: + +```cpp +#include <bits/stdc++.h> +using namespace std; + +#define f first +#define s second + +int main() { + int M = 4; + vector<pair<int,pair<int,int>>> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({w,{a,b}}); + } + sort(begin(v),end(v)); + for (auto e: v) cout << e.s.f << " " << e.s.s << " " << e.f << "\n"; +} +``` + +or a `vector` of `array<int,3>`s or `vector<int>`s: + +```cpp +int main() { + int M = 4; + vector<array<int,3>> v; // or vector<vector<int>> + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({w,a,b}); + } + sort(begin(v),end(v)); + for (auto e: v) cout << e[1] << " " << e[2] << " " << e[0] << "\n"; +} +``` + +In Python, we can use a list of lists. + +But in Java, we can't sort an `ArrayList` of `ArrayList`s without writing some additional code. What should we do? + + - If we only stored the edge weights and sorted them, we would have a sorted list of edge weights, but it would be impossible to tell which weights corresponded to which edges. + - However, if we create a **class** representing the edges and define a **custom comparator** to sort them by weight, we can sort the edges in ascending order while also keeping track of their endpoints. + + +## Classes + +First, we need to define a **class** that represents what we want to sort. In our example we will define a class `Edge` that contains the two endpoints of the edge and the weight. + +<LanguageSection> + +<CPPSection> + +### C++ + +A C++ `struct` is the same as a class in C++, but all members are public by default. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; +}; + +/* alternatively, +class Edge { + public: + int a,b,w; +}; +*/ + +int main() { + int M = 4; + vector<Edge> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({a,b,w}); + } + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +</CPPSection> + +<JavaSection> + +```java +import java.util.*; + +public class Sol { + static class Edge { + int a,b,w; + public Edge(int _a, int _b, int _w) { a = _a; b = _b; w = _w; } + } + public static void main(String[] args) { + int M = 4; + Scanner in = new Scanner(System.in); + ArrayList<Edge> v = new ArrayList<Edge>(); + for (int i = 0; i < M; ++i) { + int a = in.nextInt(); + int b = in.nextInt(); + int w = in.nextInt(); + v.add(new Edge(a,b,w)); + } + for (Edge e: v){ + System.out.print(e.a); + System.out.print(' '); + System.out.print(e.b); + System.out.print(' '); + System.out.println(e.w); + } + } +} +``` + +</JavaSection> + +<PySection> + +```py +class Edge: + def __init__(self, a, b, w): + self.a = a + self.b = b + self.w = w + +v = [] +M = 4 +for i in range(M): + a,b,w = map(int,input().split()) + v.append(Edge(a,b,w)) +for e in v: + print(e.a,e.b,e.w) +``` + +</PySection> +</LanguageSection> + +## Comparators + +Normally, sorting functions rely on moving objects with a lower value in front of objects with a higher value if sorting in ascending order, and vice versa if in descending order. This is done through comparing two objects at a time. + +<LanguageSection> + +<CPPSection> + +What a comparator does is compare two objects as follows, based on our comparison criteria: + + - If object $x$ is less than object $y$, return `true` + - If object $x$ is greater than or equal to object $y$, return `false` + +Essentially, the comparator determines whether object $x$ belongs to the left of object $y$ in a sorted ordering. A comparator **must** return false for two identical objects (not doing so results in undefined behavior and potentially a runtime error). + +In addition to returning the correct answer, comparators should also satisfy the following conditions: + + - The function must be consistent with respect to reversing the order of the arguments: if $x \neq y$ and `compare(x, y)` is `true`, then `compare(y, x)` should be `false` and vice versa. + - The function must be transitive. If `compare(x, y)` is true and `compare(y, z)` is true, then `compare(x, z)` should also be true. If the first two compare functions both return `false`, the third must also return `false`. + +### Method 1: Overloading the Less Than Operator + +This is the easiest to implement. However, it only works for objects (not primitives) and it doesn't allow you to define multiple ways to compare the same type of class. + +In the context of Wormhole Sort (note the use of +[const Edge&](https://stackoverflow.com/questions/11805322/why-should-i-use-const-t-instead-of-const-t-or-t)): + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; + bool operator<(const Edge& y) { return w < y.w; } +}; + +int main() { + int M = 4; + vector<Edge> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({a,b,w}); + } + sort(begin(v),end(v)); + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +We can also overload the operator outside of the class: + +```cpp +struct Edge { + int a,b,w; +}; +bool operator<(const Edge& x, const Edge& y) { return x.w < y.w; } +``` + +or within it using [friend](https://www.geeksforgeeks.org/friend-class-function-cpp/): + +```cpp +struct Edge { + int a,b,w; + friend bool operator<(const Edge& x, const Edge& y) { return x.w < y.w; } +}; +``` + + +### Method 2: Comparison Function + +This works for both objects and primitives, and you can declare many different comparators for the same object. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; +}; + +bool cmp(const Edge& x, const Edge& y) { return x.w < y.w; } + +int main() { + int M = 4; + vector<Edge> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({a,b,w}); + } + sort(begin(v),end(v),cmp); + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + + +We can also use [lambda expressions](../intro/lambda) in C++11 or above: + +```cpp +sort(begin(v),end(v),[](const Edge& x, const Edge& y) { return x.w < y.w; }); +``` + +</CPPSection> + +<JavaSection> + +What a `Comparator` does is compare two objects as follows, based on our comparison criteria: + +- If object $x$ is less than object $y$, return a negative integer. +- If object $x$ is greater than object $y$, return a positive integer. +- If object $x$ is equal to object $y$, return 0. + +In addition to returning the correct number, comparators should also satisfy the following conditions: + +- The function must be consistent with respect to reversing the order of the arguments: if `compare(x, y)` is positive, then `compare(y, x)` should be negative and vice versa. +- The function must be transitive. If `compare(x, y) > 0` and `compare(y, z) > 0`, then `compare(x, z) > 0`. Same applies if the compare functions return negative numbers. +- Equality must be consistent. If `compare(x, y) = 0`, then `compare(x, z)` and `compare(y, z)` must both be positive, both negative, or both zero. Note that they don't have to be equal, they just need to have the same sign. + +Java has default functions for comparing `int`, `long`, `double` types. The `Integer.compare()`, `Long.compare()`, and `Double.compare()` functions take two arguments $x$ and $y$ and compare them as described above. + +There are two ways of implementing this in Java: `Comparable`, and `Comparator`. They essentially serve the same purpose, but `Comparable` is generally easier and shorter to code. `Comparable` is a function implemented within the class containing the custom object, while `Comparator` is its own class. + +### Method 1: Comparable + +We'll need to put `implements Comparable<Edge>` into the heading of the class. Furthermore, we'll need to implement the `compareTo` method. Essentially, `compareTo(x)` is the `compare` function that we described above, with the object itself as the first argument, or `compare(self, x)`. + +When using Comparable, we can just call `Arrays.sort(arr)` or `Collections.sort(list)` on the array or list as usual. + +```java +import java.util.*; + +public class Sol { + static class Edge implements Comparable<Edge> { + int a,b,w; + public Edge(int _a, int _b, int _w) { a = _a; b = _b; w = _w; } + public int compareTo(Edge y) { return Integer.compare(w,y.w); } + } + public static void main(String[] args) { + int M = 4; + Scanner in = new Scanner(System.in); + ArrayList<Edge> v = new ArrayList<Edge>(); + for (int i = 0; i < M; ++i) { + int a = in.nextInt(); + int b = in.nextInt(); + int w = in.nextInt(); + v.add(new Edge(a,b,w)); + } + Collections.sort(v); + for (Edge e: v){ + System.out.print(e.a); + System.out.print(' '); + System.out.print(e.b); + System.out.print(' '); + System.out.println(e.w); + } + } +} +``` + +### Method 2: Comparator + +If instead we choose to use `Comparator`, we'll need to declare a second class that implements `Comparator<Edge>`: + +```java +import java.util.*; + +public class Sol { + static class Edge { + int a,b,w; + public Edge(int _a, int _b, int _w) { a = _a; b = _b; w = _w; } + } + static class Comp implements Comparator<Edge> { + public int compare(Edge a, Edge b) { + return Integer.compare(a.w, b.w); + } + } + public static void main(String[] args) { + int M = 4; + Scanner in = new Scanner(System.in); + ArrayList<Edge> v = new ArrayList<Edge>(); + for (int i = 0; i < M; ++i) { + int a = in.nextInt(); + int b = in.nextInt(); + int w = in.nextInt(); + v.add(new Edge(a,b,w)); + } + Collections.sort(v, new Comp()); + for (Edge e: v){ + System.out.print(e.a); + System.out.print(' '); + System.out.print(e.b); + System.out.print(' '); + System.out.println(e.w); + } + } +} +``` + +When using `Comparator`, the syntax for using the built-in sorting function requires a second argument: `Arrays.sort(arr, new Comp())`, or `Collections.sort(list, new Comp())`. + +</JavaSection> + +<PySection> + +### Defining Less Than Operator + +<!-- Tested --> +```py +class Edge: + def __init__(self, a, b, w): + self.a = a + self.b = b + self.w = w + def __lt__(self, y): # lt means less than + return self.w < y.w + +v = [] +M = 4 +for i in range(M): + a,b,w = map(int,input().split()) + v.append(Edge(a,b,w)) +v.sort() +for e in v: + print(e.a,e.b,e.w) +``` + +### Key Function + +This method maps an object to another comparable datatype with which to be sorted. In this case we map edges to their weights. + +```py +class Edge: + def __init__(self, a, b, w): + self.a = a + self.b = b + self.w = w + +v = [] +M = 4 +for i in range(M): + a,b,w = map(int,input().split()) + v.append(Edge(a,b,w)) +v.sort(key=lambda x:x.w) +for e in v: + print(e.a,e.b,e.w) +``` + +### Comparison Function + +A comparison function in Python must satisfy the same properties as a comparator in Java. Note that old-style cmp functions are [no longer supported](https://stackoverflow.com/questions/12749398/using-a-comparator-function-to-sort), so the comparison function must be converted into a key function with [`cmp_to_key`](https://docs.python.org/2/library/functools.html). + +```py +from functools import cmp_to_key + +class Edge: + def __init__(self, a, b, w): + self.a = a + self.b = b + self.w = w + +v = [] +M = 4 +for i in range(M): + a,b,w = map(int,input().split()) + v.append(Edge(a,b,w)) + +v.sort(key=cmp_to_key(lambda a,b: a.w-b.w)) +for e in v: + print(e.a,e.b,e.w) +``` + +</PySection> + +</LanguageSection> + +## Variations + +### Sorting in Decreasing Order of Weight + +We can replace all occurrences of `x.w < y.w` with `x.w > y.w` in our C++ code. Similarly, we can replace all occurrences of `Integer.compare(x, y)` with `-Integer.compare(x, y)` in our Java code or negate the key in Python. + +### Sorting by Two Criteria + +Now, suppose we wanted to sort a list of `Edge`s in ascending order, primarily by weight and secondarily by first vertex (`a`). We can do this quite similarly to how we handled sorting by one criterion earlier. What the comparator function needs to do is to compare the weights if the weights are not equal, and otherwise compare first vertices. + +<LanguageSection> + +<CPPSection> + +```cpp +struct Edge { + int a,b,w; + bool operator<(const Edge& y) { + if (w != y.w) return w < y.w; + return a < y.a; + } +}; +``` + +</CPPSection> + +<JavaSection> + +```java +static class Edge implements Comparable<Edge> { + int a,b,w; + public Edge(int _a, int _b, int _w) { a = _a; b = _b; w = _w; } + public int compareTo(Edge y) { + if (w != y.w) return Integer.compare(w,y.w); + return Integer.compare(a,y.a); + } +} +``` + +</JavaSection> + +<PySection> + +```py +class Edge: + def __init__(self, a, b, w): + self.a = a + self.b = b + self.w = w + def __lt__(self, y): # lt means less than + return self.w < y.w if self.w != y.w else self.a < y.a + +``` + +</PySection> + +</LanguageSection> + +Sorting by an arbitrary number of criteria is done similarly. + +<LanguageSection> + +<CPPSection> + + + +</CPPSection> + +<JavaSection> + +With Java, we can implement a comparator for arrays of arbitrary length (although this might be more confusing than creating a separate class). + +```java +import java.util.*; + +public class Sol { + static class Comp implements Comparator<int[]> { + public int compare(int[] a, int[] b){ + for (int i = 0; i < a.length; ++i) + if (a[i] != b[i]) return Integer.compare(a[i],b[i]); + return 0; + } + } + public static void main(String[] args) { + int M = 4; + Scanner in = new Scanner(System.in); + ArrayList<int[]> v = new ArrayList<int[]>(); + for (int i = 0; i < M; ++i) { + int a = in.nextInt(); + int b = in.nextInt(); + int w = in.nextInt(); + v.add(new int[]{w,a,b}); + } + Collections.sort(v, new Comp()); + for (int[] e: v){ + System.out.print(e[1]); + System.out.print(' '); + System.out.print(e[2]); + System.out.print(' '); + System.out.println(e[0]); + } + } +} +``` + +</JavaSection> + +<PySection> + +</PySection> + +</LanguageSection> + +## Problems + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/3_Silver/Sorting_Methods.txt b/content/3_Silver/Sorting_Methods.txt new file mode 100644 index 0000000..bebe63d --- /dev/null +++ b/content/3_Silver/Sorting_Methods.txt @@ -0,0 +1,47 @@ +--- +id: sorting-methods +title: "Sorting Methods" +author: Nathan Chen +description: "Methods for efficently sorting an array." +prerequisites: + - intro-ds +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + bubble: [ + new Problem("Silver", "Out of Sorts", "834", "Very Hard", false, []), + ], +}; + +<Resources> + <Resource source="CPH" title="3.1 - Sorting Theory" starred>types of sorting</Resource> +</Resources> + +<br /> + +(why are these important?) + +<IncompleteSection /> + +There are many sorting algorithms, here are some sources to learn about the popular ones: + +## Bubble Sort + +<Problems problems={problems.bubble} /> + +### Tutorial + + - [HackerEarth Quicksort](https://www.hackerearth.com/practice/algorithms/sorting/quick-sort/tutorial/) + - expected $O(N\log N)$ + - [HackerEarth Mergesort](https://www.hackerearth.com/practice/algorithms/sorting/merge-sort/tutorial/) + - $O(N\log N)$ + + +## Problems + +??? + +<IncompleteSection />
\ No newline at end of file diff --git a/content/3_Silver/Sorting_Old.txt b/content/3_Silver/Sorting_Old.txt new file mode 100644 index 0000000..0dcbbef --- /dev/null +++ b/content/3_Silver/Sorting_Old.txt @@ -0,0 +1,447 @@ +--- +id: sorting-old +title: "Sorting with Custom Comparators Pt 1" +author: Darren Yao, Siyong Huang, Michael Cao, Benjamin Qi +prerequisites: + - Bronze - Pairs & Tuples in C++ + - Bronze - Introduction to Data Structures +description: "Both Java and C++ have built-in functions for sorting. However, if we use custom objects, or if we want to sort elements in a different order, then we'll need to use a custom comparator." +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("Silver", "Wormhole Sort", "992", "Normal", false, [], ""), + ], + general: [ + new Problem("CSES", "Restaurant Customers", "1619", "Easy", false, [], "sort endpoints of intervals"), + new Problem("Silver", "Lifeguards", "786", "Easy", false, [], "sort endpoints of intervals"), + 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("Silver", "Meetings", "967", "Very Hard", false, [], ""), + ], +}; + +## Example: Wormhole Sort + +<Problems problems={problems.sample} /> + +There are multiple ways to solve this problem. We won't discuss the full solution here, but all of them start by sorting the edges in nondecreasing order of weight. + +With C++, the easiest method is to use nested pairs. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +#define f first +#define s second + +int main() { + int M = 4; + vector<pair<int,pair<int,int>>> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({w,{a,b}}); + } + sort(begin(v),end(v)); + for (auto e: v) cout << e.s.f << " " << e.s.s << " " << e.f << "\n"; +} +``` + +But what if the built-in comparison function for pairs didn't exist? + + - If we only stored the edge weights and sorted them, we would have a sorted list of edge weights, but it would be impossible to tell which weights corresponded to which edges. + - However, if we create a **class** (or struct) representing the edges and define a **custom comparator** to sort them by weight, we can sort the edges in ascending order while also keeping track of their endpoints. + + +## Classes + +First, we need to define a **class** that represents what we want to sort (or a [`struct`](http://www.cplusplus.com/doc/tutorial/structures/) in C++, which is the same as a `class` in C++ but all members are public by default). + +In our example we will define a class `Person` that contains a person's height and weight, and sort in ascending order by height. + +```cpp +struct Person { + int height, weight; + Person (int h, int w) { height = h; weight = w; } +}; + +int main() { + Person p; + p.height = 60; // assigns 60 to the height of p + p.weight = 100; // assigns 100 to the weight of p +} +``` +```java +static class Person { + int height, weight; + public Person (int h, int w) { height = h; weight = w; } +} +``` + +## Comparators + +Normally, sorting functions rely on moving objects with a lower value in front of objects with a higher value if sorting in ascending order, and vice versa if in descending order. This is done through comparing two objects at a time. + +### C++ + +<Resources> + <Resource source="CPH" title="3.2 - User-Defined Structs, Comparison Functions" starred></Resource> +</Resources> + +What a comparator does is compare two objects as follows, based on our comparison criteria: + + - If object $x$ is less than object $y$, return `true` + - If object $x$ is greater than or equal to object $y$, return `false` + +Essentially, the comparator determines whether object $x$ belongs to the left of object $y$ in a sorted ordering. A comparator **must** return false for two identical objects (not doing so results in undefined behavior and potentially a runtime error). + +In addition to returning the correct answer, comparators should also satisfy the following conditions: + + - The function must be consistent with respect to reversing the order of the arguments: if $x \neq y$ and `compare(x, y)`is `true`, then `compare(y, x)` should be `false` and vice versa. + - The function must be transitive. If `compare(x, y)` is true and `compare(y, z)` is true, then `compare(x, z)` should also be true. If the first two compare functions both return `false`, the third must also return `false`. + +### Java + +What a `Comparator` does is compare two objects as follows, based on our comparison criteria: + +- If object $x$ is less than object $y$, return a negative number. +- If object $x$ is greater than object $y$, return a positive number. +- If object $x$ is equal to object $y$, return 0. + +In addition to returning the correct number, comparators should also satisfy the following conditions: +- The function must be consistent with respect to reversing the order of the arguments: if `compare(x, y)` is positive, then `compare(y, x)` should be negative and vice versa. +- The function must be transitive. If `compare(x, y) > 0` and `compare(y, z) > 0`, then `compare(x, z) > 0`. Same applies if the compare functions return negative numbers. +- Equality must be consistent. If `compare(x, y) = 0`, then `compare(x, z)` and `compare(y, z)` must both be positive, both negative, or both zero. Note that they don't have to be equal, they just need to have the same sign. + +Java has default functions for comparing `int`, `long`, `double` types. The `Integer.compare()`, `Long.compare()`, and `Double.compare()` functions take two arguments $x$ and $y$ and compare them as described above. + +## C++ + +<Resources> + <Resource source="fushar" title="Comparison Functions in C++" url="http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/"></Resource> +</Resources> + +### Method 1: Operator < + +[StackOverflow: Why const T&?](https://stackoverflow.com/questions/11805322/why-should-i-use-const-t-instead-of-const-t-or-t) + + - Pro: + - This is the easiest to implement + - Easy to work with STL + - Con: + - Only works for objects (not primitives) + - Only supports two types of comparisons (less than (<) and greater than (>)) + +<!-- Tested --> +```cpp +#include <bits/stdc++.h> +using namespace std; +int randint(int low, int high) {return low+rand()%(high-low);} + +struct Foo +{ + int Bar; + Foo(int _Bar=-1):Bar(_Bar){} + bool operator < (const Foo& foo2) const {return Bar < foo2.Bar;} +}; +const int N = 8; +int main() +{ + srand(69); + Foo a[N]; + for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); + sort(a, a+N); + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); +} + +/** +Output: +(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0) +(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18) +*/ +``` + +In the context of Wormhole Sort (uses [friend](https://www.geeksforgeeks.org/friend-class-function-cpp/)): + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { + int a,b,w; + friend bool operator<(const Edge& x, const Edge& y) { return x.w < y.w; } +}; // a different way to write less than + +int main() { + int M = 4; + vector<Edge> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({a,b,w}); + } + sort(begin(v),end(v)); + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} + +/* +Input: +1 2 9 +1 3 7 +2 3 10 +2 4 3 +*/ + +/* +Output: +2 4 3 +1 3 7 +1 2 9 +2 3 10 +*/ +``` + +### Method 2: Function Outside Class + +Let's say we have an array `Person arr[N]`. To sort the array, we need to make custom comparator which will be a function, and then pass the function as a parameter into the build-in sort function: + +```cpp +bool cmp(Person a, Person b) { + return a.height < b.height; +} + +int main() { + sort(arr, arr+N, cmp); // sorts the array in ascending order by height +} +``` + +If we instead wanted to sort in descending order, this is also very simple. Instead of the `cmp` function returning `return a.height < b.height;`, it should do `return a.height > b.height;`. + + - Pro: + - Works for both objects and primitives + - Supports many different comparators for the same object + - Con: + - More difficult to implement + - Extra care needs to be taken to support STL + +We can also use [lambda expressions](https://www.geeksforgeeks.org/lambda-expression-in-c/) in C++11 or above. + +<!-- Tested --> + +```cpp +#include <bits/stdc++.h> +using namespace std; +int randint(int low, int high) {return low+rand()%(high-low);} + +struct Foo +{ + int Bar; + Foo(int _Bar=-1):Bar(_Bar){} +}; +const int N = 8; +Foo a[N]; +bool cmp1(Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;} +function<bool(Foo,Foo)> cmp2 = [](Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;}; // lambda expression +// bool(Foo,Foo) means that the function takes in two parameters of type Foo and returns bool +// "function<bool(Foo,Foo)>"" can be replaced with "auto" + +int main() +{ + srand(69); + + printf("--- Method 1 ---\n"); + for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); + sort(a, a+N, [](Foo foo1, Foo foo2){return foo1.Bar<foo2.Bar;}); // lambda expression + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); + + printf("--- Method 2 ---\n"); + for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); + sort(a, a+N, cmp1); + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); + + printf("--- Method 3 ---\n"); + for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); + sort(a, a+N, cmp2); + for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); +} +``` + +Output: + +``` +--- Method 1 --- +(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0) +(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18) +--- Method 2 --- +(Foo: 5) (Foo: 13) (Foo: 18) (Foo: 0) (Foo: 9) (Foo: 4) (Foo: 2) (Foo: 15) +(Foo: 0) (Foo: 2) (Foo: 4) (Foo: 5) (Foo: 9) (Foo: 13) (Foo: 15) (Foo: 18) +--- Method 3 --- +(Foo: 1) (Foo: 1) (Foo: 18) (Foo: 0) (Foo: 11) (Foo: 12) (Foo: 1) (Foo: 5) +(Foo: 0) (Foo: 1) (Foo: 1) (Foo: 1) (Foo: 5) (Foo: 11) (Foo: 12) (Foo: 18) +``` + +In the context of Wormhole Sort: + +```cpp +#include <bits/stdc++.h> +using namespace std; + +struct Edge { int a,b,w; }; + +int main() { + int M = 4; + vector<Edge> v; + for (int i = 0; i < M; ++i) { + int a,b,w; cin >> a >> b >> w; + v.push_back({a,b,w}); + } + sort(begin(v),end(v),[](const Edge& x, const Edge& y) { return x.w < y.w; }); + for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; +} +``` + +## Java + +Now, there are two ways of implementing this in Java: `Comparable`, and `Comparator`. They essentially serve the same purpose, but `Comparable` is generally easier and shorter to code. `Comparable` is a function implemented within the class containing the custom object, while `Comparator` is its own class. For our example, we'll use a `Person` class that contains a person's height and weight, and sort in ascending order by height. + +### Comparable + +If we use `Comparable`, we'll need to put `implements Comparable<Person>` into the heading of the class. Furthermore, we'll need to implement the `compareTo` method. Essentially, `compareTo(x)` is the `compare` function that we described above, with the object itself as the first argument, or `compare(self, x)`. + +```java +static class Person implements Comparable<Person>{ + int height, weight; + public Person(int h, int w){ + height = h; weight = w; + } + public int compareTo(Person p){ + return Integer.compare(height, p.height); + } +} +``` + +When using Comparable, we can just call `Arrays.sort(arr)` or `Collections.sort(list)` on the array or list as usual. + +### Comparator + +If instead we choose to use `Comparator`, we'll need to declare a second `Comparator` class, and then implement that: + +```java +static class Person{ + int height, weight; + public Person(int h, int w){ + height = h; weight = w; + } +} +static class Comp implements Comparator<Person>{ + public int compare(Person a, Person b){ + return Integer.compare(a.height, b.height); + } +} +``` + +When using `Comparator`, the syntax for using the built-in sorting function requires a second argument: `Arrays.sort(arr, new Comp())`, or `Collections.sort(list, new Comp())`. + +If we instead wanted to sort in descending order, this is also very simple. Instead of the comparing function returning `Integer.compare(x, y)` of the arguments, it should instead return `-Integer.compare(x, y)`. + +## Python + +### Defining Operator + +<!-- Tested --> +```py +import random +class Foo: + def __init__(self, _Bar): self.Bar = _Bar + def __str__(self): return "Foo({})".format(self.Bar) + def __lt__(self, o): # lt means less than + return self.Bar < o.Bar + +a = [] +for i in range(8): + a.append(Foo(random.randint(1, 10))) +print(*a) +print(*sorted(a)) +``` + +Output: + +``` +Foo(0) Foo(1) Foo(2) Foo(1) Foo(9) Foo(5) Foo(5) Foo(8) +Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9) +``` + +### Remapping Key + + - This method maps an object to another comparable datatype with which to be sorted. In this case, `Foo` is sorted by the sum of its members `x` and `y`. + + +<!-- Tested --> +```py +import random +class Foo: + def __init__(self, _Bar, _Baz): self.Bar,self.Baz = _Bar,_Baz + def __str__(self): return "Foo({},{})".format(self.Bar, self.Baz) + +a = [] +for i in range(8): + a.append(Foo(random.randint(1, 9)*10, random.randint(1, 9))) +print(*a) + +print(*sorted(a, key=lambda foo: foo.Bar+foo.Baz)) +def key(foo): + return foo.Bar + foo.Baz +print(*sorted(a, key=key)) +``` + +Output: + +``` +Foo(10,2) Foo(30,2) Foo(60,6) Foo(90,7) Foo(80,7) Foo(80,9) Foo(60,9) Foo(90,8) +Foo(10,2) Foo(30,2) Foo(60,6) Foo(60,9) Foo(80,7) Foo(80,9) Foo(90,7) Foo(90,8) +Foo(10,2) Foo(30,2) Foo(60,6) Foo(60,9) Foo(80,7) Foo(80,9) Foo(90,7) Foo(90,8) +``` + +#### Function / Lambda + + - This method defines how to compare two elements represented by an integer + - Positive: First term is greater than the second term + - Zero: First term and second term are equal + - Negative: First term is less than the second term + +Note how the comparator must be converted to a `key`. + +<!-- Tested --> + +```py +import random +from functools import cmp_to_key +class Foo: + def __init__(self, _Bar): self.Bar = _Bar + def __str__(self): return "Foo({})".format(self.Bar) + +a = [] +for i in range(8): + a.append(Foo(random.randint(0, 9))) +print(*a) + +print(*sorted(a, key=cmp_to_key(lambda foo1, foo2: foo1.Bar - foo2.Bar))) +def cmp(foo1, foo2): + return foo1.Bar - foo2.Bar +print(*sorted(a, key=cmp_to_key(cmp))) +``` + +Output: + +``` +Foo(0) Foo(1) Foo(2) Foo(1) Foo(9) Foo(5) Foo(5) Foo(8) +Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9) +Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9) +```
\ No newline at end of file diff --git a/content/3_Silver/Unordered.mdx b/content/3_Silver/Unordered.mdx new file mode 100644 index 0000000..4983dc3 --- /dev/null +++ b/content/3_Silver/Unordered.mdx @@ -0,0 +1,257 @@ +--- +id: unordered +title: Unordered Maps & Sets +author: Darren Yao, Benjamin Qi +description: "With hashing." +frequency: 2 +prerequisites: + - pairs-tuples +--- + +import { Problem } from "../models"; + +export const problems = { + dis: [ + new Problem("CSES", "Distinct Numbers", "1621", "Easy", false, [], "Store every number in a set and print the size."), + ], + ex: [ + new Problem("YS", "Associative Array", "associative_array", "Easy") + ], + standard: [ + new Problem("CSES", "Sum of Two Values", "1640", "Easy", false, [], "Brute force one value by going through a[], then check if the other exists."), + new Problem("Bronze", "Where Am I?", "964", "Easy", false, [], "Store all substrings in a map of <string, count>, and then find the longest length such that no substring of that length appears twice."), + new Problem("Silver", "Bovine Genomics", "739", "Easy", false, [], ""), + new Problem("Silver", "Cities & States", "667", "Normal", false, [], "Store two maps of counts for the first two letters of a city and state code, then iterate over the cities and use the maps to efficently query for the corresponding counts."), + ], +}; + +<Resources> + <Resource source="IUSACO" title="4.3 - Sets & Maps">module is based off this</Resource> +</Resources> + +## What Are Sets and Maps? + +A **set** is a collection of objects that contains no duplicates. A **map** is a set of ordered pairs, each containing a key and a value. In a map, all keys are required to be unique, but values can be repeated. Maps have three primary methods: one to add a specified key-value pairing, one to retrieve the value for a given key, and one to remove a key-value pairing from the map. Like sets, maps can be unordered or ordered. + +Both Java and C++ contain two versions of sets and maps; one in which the keys are stored in sorted order, and one in which **hashing** is used. Bronze problems shouldn't distinguish between the two, so we'll cover only the latter in this module. + +## Hashing + +**Hashing** refers to assigning a unique code to every variable/object which allows insertions, deletions, and searches in $O(1)$ time, albeit with a high constant factor, as hashing requires a large constant number of operations. However, as the name implies, elements are not ordered in any meaningful way, so traversals of an unordered set will return elements in some arbitrary order. + +(more in-depth explanation?) + +<IncompleteSection /> + +## Sets + +<Problems problems={problems.dis} /> + +<LanguageSection> + +<CPPSection> + +The operations on an [`unordered_set`](http://www.cplusplus.com/reference/unordered_set/unordered_set/) are `insert`, which adds an element to the set if not already present, `erase`, which deletes an element if it exists, and `count`, which returns `1` if the set contains the element and `0` if it doesn't. + +```cpp +unordered_set<int> s; +s.insert(1); // [1] +s.insert(4); // [1, 4] in arbitrary order +s.insert(2); // [1, 4, 2] in arbitrary order +s.insert(1); // [1, 4, 2] in arbitrary order +// the add method did nothing because 1 was already in the set +cout << s.count(1) << endl; // 1 +set.erase(1); // [2, 4] in arbitrary order +cout << s.count(5) << endl; // 0 +s.erase(0); // [2, 4] in arbitrary order +// if the element to be removed does not exist, nothing happens + +for(int element : s){ + cout << element << " "; +} +cout << endl; +// You can iterate through an unordered set, but it will do so in arbitrary order +``` + +</CPPSection> + +<JavaSection> + +The operations on a `HashSet` are `add`, which adds an element to the set if not already present, `remove`, which deletes an element if it exists, and `contains`, which checks whether the set contains that element. + +```java +HashSet<Integer> set = new HashSet<Integer>(); +set.add(1); // [1] +set.add(4); // [1, 4] in arbitrary order +set.add(2); // [1, 4, 2] in arbitrary order +set.add(1); // [1, 4, 2] in arbitrary order +// the add method did nothing because 1 was already in the set +System.out.println(set.contains(1)); // true +set.remove(1); // [2, 4] in arbitrary order +System.out.println(set.contains(5)); // false +set.remove(0); // [2, 4] in arbitrary order +// if the element to be removed does not exist, nothing happens + +for(int element : set){ + System.out.println(element); +} +// You can iterate through an unordered set, but it will do so in arbitrary order +``` + +</JavaSection> + +</LanguageSection> + +## Maps + +<Problems problems={problems.ex} /> + +<LanguageSection> + +<CPPSection> + +In an [`unordered_map`](http://www.cplusplus.com/reference/unordered_map/unordered_map/) `m`, the `m[key] = value` operator assigns a value to a key and places the key and value pair into the map. The operator `m[key]` returns the value associated with the key. If the key is not present in the map, then `m[key]` is set to 0. The `count(key)` method returns the number of times the key is in the map (which is either one or zero), and therefore checks whether a key exists in the map. Lastly, `erase(key)` and `erase(it)` removes the map entry associated with the specified key or iterator. All of these operations are $O(1)$, but again, due to the hashing, this has a high constant factor. + +```cpp +unordered_map<int, int> m; +m[1] = 5; // [(1, 5)] +m[3] = 14; // [(1, 5); (3, 14)] +m[2] = 7; // [(1, 5); (3, 14); (2, 7)] +m.erase(2); // [(1, 5); (3, 14)] +cout << m[1] << '\n'; // 5 +cout << m.count(7) << '\n' ; // 0 +cout << m.count(1) << '\n' ; // 1 +``` + +</CPPSection> + +<JavaSection> + +In a `HashMap`, the `put(key, value)` method assigns a value to a key and places the key and value pair into the map. The `get(key)` method returns the value associated with the key. The `containsKey(key)` method checks whether a key exists in the map. Lastly, `remove(key)` removes the map entry associated with the specified key. All of these operations are $O(1)$, but again, due to the hashing, this has a high constant factor. + +```java +HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); +map.put(1, 5); // [(1, 5)] +map.put(3, 14); // [(1, 5); (3, 14)] +map.put(2, 7); // [(1, 5); (3, 14); (2, 7)] +map.remove(2); // [(1, 5); (3, 14)] +System.out.println(map.get(1)); // 5 +System.out.println(map.containsKey(7)); // false +System.out.println(map.containsKey(1)); // true +``` + +</JavaSection> + +</LanguageSection> + +(iterating over map?) + +### Custom Hashing + +There is no built in method for hashing pairs or vectors. Namely, `unordered_set<vector<int>>` does not work. In this case, we can use an [ordered map](../silver/intro-ordered) (which supports all of the functions used in the code above) or declare our own hash function. + +<LanguageSection> + +<CPPSection> + +<Resources> + <Resource source="Mark Nelson" title="Hash Functions for C++ Unordered Containers" url="https://marknelson.us/posts/2011/09/03/hash-functions-for-c-unordered-containers.html" starred>How to create user-defined hash function for `unordered_map`.</Resource> +</Resources> + +The link provides an example of hashing pairs of strings. More examples (for pairs of ints) + +```cpp +#include <bits/stdc++.h> +using namespace std; + +typedef pair<int,int> pi; +#define f first +#define s second + +struct hashPi { + size_t operator()(const pi& p) const { return p.f^p.s; } +}; + +int main() { + unordered_map<pi,int,hashPi> um; + +} +``` + +```cpp +#include <bits/stdc++.h> +using namespace std; + +typedef pair<int,int> pi; +#define f first +#define s second + +namespace std { + template<> struct hash<pi> { + size_t operator()(const pi& p) const { return p.f^p.s; } + }; +} + +int main() { + unordered_map<pi,int> um; + +} +``` + +However, this hash function is quite bad; if we insert $(0,0), (1,1), (2,2) \ldots$ then they will all be mapped to the same bucket (so it would easily be **hacked**). + +</CPPSection> + +</LanguageSection> + +## Hacking + +<Warning> + +You don't need to know this for USACO, but you will need this to pass some of the problems in this module. + +</Warning> + +In USACO contests, unordered sets and maps generally fine, but the built-in hashing algorithm for C++ is vulnerable to pathological data sets causing abnormally slow runtimes. Apparently [Java](https://codeforces.com/blog/entry/62393?#comment-464875) is not vulnerable to this, however. + +<LanguageSection> + +<CPPSection> + +<Resources> + <Resource title="Blowing up Unordered Map" source="CF" url="blog/entry/62393" starred>Explanation of this problem and how to fix it.</Resource> +</Resources> + +Essentially use `unordered_map<int, int, custom_hash>` defined in the blog above in place of `unordered_map<int, int>`. + +### Another Hash Function + +<Resources> + <Resource source="Benq (from KACTL)" title="HashMap" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/STL%20(5)/HashMap.h" starred> </Resource> +</Resources> + +```cpp +struct chash { /// use most bits rather than just the lowest ones + const uint64_t C = ll(2e18*PI)+71; // large odd number + const int RANDOM = rng(); // random 32-bit number + ll operator()(ll x) const { + // https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html + return __builtin_bswap64((x^RANDOM)*C); + } +}; +template<class K,class V> using um = unordered_map<K,V,chash>; +``` + +(explain assumptions that are required for this to work) + +</CPPSection> + +</LanguageSection> + +### `gp_hash_table` + +Mentioned in several of the links above. See [Gold](../gold/faster-hashmap) for details. + +## Problems + +<Problems problems={problems.standard} /> diff --git a/content/4_Gold/.DS.swp b/content/4_Gold/.DS.swp Binary files differnew file mode 100644 index 0000000..432d1ef --- /dev/null +++ b/content/4_Gold/.DS.swp diff --git a/content/4_Gold/BFS.mdx b/content/4_Gold/BFS.mdx new file mode 100644 index 0000000..d629d3e --- /dev/null +++ b/content/4_Gold/BFS.mdx @@ -0,0 +1,223 @@ +--- +id: bfs +title: "Breadth First Search (BFS)" +author: Benjamin Qi, Michael Cao +prerequisites: + - dfs +description: "Traversing a graph in a way such that vertices closer to the starting vertex are processed first." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Message Route", "1667", "Easy", false, ["BFS"]), + ], + general: [ + new Problem("CSES", "Monsters", "1194", "Easy", false, ["BFS"]), + 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"]), + ] +}; + +<Problems problems={problems.sample} /> + +## Resources + +<Resources> + <Resource source="CSA" title="Breadth First Search" url="breadth_first_search" starred>interactive</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> + <Resource source="cp-algo" title="0/1 BFS" url="graph/01_bfs.html"></Resource> + <Resource source="IUSACO" title="10.4 - Graph Traversal Algorithms"></Resource> +</Resources> + +## Implementation + +<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> + +Implementation of the CSAcademy article's problem in Java: + +```java +import java.util.*; +import java.io.*; + +class Main { + + static ArrayList<Integer> edges[]; + static int dist[]; + static boolean visited[]; + + static void bfs(int startNode) { + Queue<Integer> q = new ArrayDeque<Integer>(); //You can choose any implementation of Queue (such as LinkedList), though I believe ArrayDeque is faster (?) + + q.add(startNode); + visited[startNode] = true; + dist[startNode] = 0; + + while(!q.isEmpty()) { + int currentNode = q.poll(); + + for(int adj : edges[currentNode]) { + if(!visited[adj]) { + visited[adj] = true; + dist[adj] = dist[currentNode]+1; + q.add(adj); + } + } + } + } + + public static void main (String[] args) throws Exception { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //Read about fast Java input in "Intro - Fast I/O" + + StringTokenizer st = new StringTokenizer(br.readLine()); + int N = Integer.parseInt(st.nextToken()); + int M = Integer.parseInt(st.nextToken()); + + int start = Integer.parseInt(st.nextToken()); + start--; + + edges = new ArrayList[N]; + dist = new int[N]; + visited = new boolean[N]; + for(int i = 0; i < N; i++) { + edges[i] = new ArrayList<Integer>(); + dist[i] = -1; + } + + for(int i = 0; i < M; i++) { + st = new StringTokenizer(br.readLine()); + int a = Integer.parseInt(st.nextToken()); + int b = Integer.parseInt(st.nextToken()); + + a--; b--; + edges[a].add(b); + } + + bfs(start); + + PrintWriter pw = new PrintWriter(System.out); + + for(int i : dist) pw.print(i + " "); + pw.println(); + pw.close(); + } +} +``` + +</JavaSection> + +</LanguageSection> + +## Example: Cow Navigation + +In the gold division, the problem statement will never directly be, "Given an unweighted graph, find the shortest path between node $u$ and $v$." Instead, the difficulty in many BFS problems are modifying the problem into a graph on which we can run BFS and get the answer. + +<Problems problems={problems.example} /> + +In this problem, Bessie stands on a grid and wants to go from the lower left corner to upper-right corner in as few moves as possible. An initial idea could be to model the grid as a graph, where adjacent cells are connected by edges, and run a BFS to find the shortest path. + +However, two additional constriants play a role in this problem: Bessie must be able to reach the destination regardless of which direction she starts in, and she can only move in the direction she is facing. + +Let's imagine now that there are two cows standing on the cell $(1, 1)$, and both of them move the same way on each operation. Since $N \leq 20$, we can modify the original graph to support this new problem. Let's create a new graph $G'$ as follows: + +For each pair of cells in the grid, $(x, y)$ and $(x_2, y_2)$, add $16$ <!-- wow that's a lot --> nodes in the graph storing six parameters each: + +- $x$ coordinate of cow $a$ +- $y$ coordinate of cow $a$ +- $x$ coordinate of cow $b$ +- $y$ coordinate of cow $b$ +- direction of cow $a$ +- direction of cow $b$ + +for all $4 ^ 2$ directions each cow could be facing. + +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 the state $\{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> + +<!-- (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? --> + + +### Implementation +<!-- I really don't want to write Cow Navigation. Anyone have code? --> + +<IncompleteSection /> + +## Problems + +<Problems problems={problems.general} /> diff --git a/content/5_Gold/Cyc.mdx b/content/4_Gold/Cyc.mdx index 62b9e15..24a9399 100644 --- a/content/5_Gold/Cyc.mdx +++ b/content/4_Gold/Cyc.mdx @@ -3,16 +3,14 @@ id: cyc title: Cycle Detection author: Siyong Huang prerequisites: - - Silver - Functional Graphs - - Gold - Breadth First Search -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. + - 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 --- import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { und: [ new Problem("CSES", "Round Trip", "1669", "Easy", false, ["Cycle"]), ], @@ -22,7 +20,6 @@ export const metadata = { general: [ new Problem("CSES", "Graph Girth", "1707", "Easy", false, ["Cycle"]), ], - } }; @@ -30,15 +27,25 @@ export const metadata = { ## Undirected Graphs -<problems-list problems={metadata.problems.und} /> +<Problems problems={problems.und} /> -BFS-Cycle? +(explanation?) + +<Optional title="+1 Approximation for Shortest Cycle"> + +An algorithm known as **BFS-Cycle** returns an integer that is at most one more than the length of the shortest cycle in $O(N^2)$ time; see page 4 [here](https://people.csail.mit.edu/virgi/6.890/lecture9.pdf) for details. + +</Optional> ## Directed Graphs -<problems-list problems={metadata.problems.dir} /> +<Problems problems={problems.dir} /> + +The same general idea is implemented below to find any cycle in a directed graph (if one exists). Note that this is almost identical to the DFS algorithm for topological sorting. -The same general idea is implemented below to find any cycle in a directed graph (if one exists). +<LanguageSection> + +<CPPSection> ```cpp //UNTESTED @@ -84,8 +91,20 @@ int main() } ``` +</CPPSection> + +<JavaSection> + + + +</JavaSection> + +</LanguageSection> + + + ## Problems -<problems-list problems={metadata.problems.general} /> +<Problems problems={problems.general} /> VT-HSPC 2019?
\ No newline at end of file diff --git a/content/4_Gold/DP.mdx b/content/4_Gold/DP.mdx new file mode 100644 index 0000000..70db325 --- /dev/null +++ b/content/4_Gold/DP.mdx @@ -0,0 +1,121 @@ +--- +id: intro-dp +title: "Introduction to Dynamic Programming (DP)" +author: Michael Cao +prerequisites: + - complete-search + - prefix-sums +description: "Speeding up naive recursive solutions with memoization." +frequency: 4 +--- + +import { Problem } from "../models"; + +export const problems = { + usacoEasy: [ + new Problem("Gold", "Hoof Paper Scissors", "694", "Easy", false, ["DP"], "dp[first i games][# changes][last gesture] -> max games won"), + new Problem("Gold", "Time is Mooney", "993", "Easy", true, ["DP", "Graphs"], "dp[time][city] -> money"), + new Problem("Gold", "Teamwork", "863", "Easy", false, ["DP"], "let dp[i] -> max sum of skill levels for the first i cows, then precompute subarray maximums"), + new Problem("Gold", "Snakes", "945", "Easy", false, ["DP"], "dp[i][j] -> minimum sum of net sizes needed to catch m snakes with k changes, precompute maximums"), + ], + usacoPast: [ + new Problem("Gold", "Circular Barn Revisited", "622", "Normal", true, ["DP", "Brute Force"], "brute force starting location, then do dp[first i positions][number of doors used][position of last door] -> minimum distance cows need to travel"), + new Problem("Gold", "Taming the Herd", "815", "Normal", false, ["DP"], "dp[consider first i entries only][last breakout in first i occurs at j][k breakouts among first i entries] -> # changes"), + new Problem("Gold", "Moortal Cowmbat", "971", "Hard", true, ["DP", "Prefix Sums", "All Pairs Shortest Path"], "dp[first i letters form valid combo][last letter] -> time, Floyd Warshall on alphabet, then use prefix sums to speed up transitions."), + new Problem("Plat", "Team Building", "673", "Hard", false, [], ""), + new Problem("Gold", "Stamp Painting", "791", "Hard", false, ["DP"], "must be K consectutive of same color, complimetary counting for dp[up to position i][number of consecutive] -> number of ways, find closed form to reduce runtime"), + ], + knapsack: [ + new Problem("CSES", "Unordered Coin Change", "1635", "Easy", true, ["DP", "Knapsack"], "dp[first i coins][sum] = number of ways, order of loops is capacity then coins, remember to take MOD after every computation"), + new Problem("CSES", "Ordered Coin Change", "1636", "Easy", true, ["DP", "Knapsack"], "dp[first i coins][sum] = number of ways, order of loops is coins then capacity, remember to take MOD after every computation"), + new Problem("CSES", "Minimum Coins", "1634", "Easy", true, ["DP", "Knapsack"], "dp[first i coins][sum] = minimum number of coins needed"), + new Problem("AC", "Knapsack 2", "contests/dp/tasks/dp_e", "Easy", false, ["DP", "Knapsack"], "maximum capacity is large, and sum of values is small, so switch the states. dp[first i items][sum of values] = minimum capacity needed to achieve this sum"), + new Problem("Gold", "Fruit Feast", "574", "Easy", false, ["DP", "Knapsack"], "dp[fullness] = whether you can achieve this fullness"), + new Problem("Gold", "Talent Show", "839", "Hard", false, ["DP", "Knapsack", "Binary Search", "Math"], "binary search on optimal ratio, then do knapsack on weight"), + new Problem("CF", "Round Subset", "http://codeforces.com/contest/837/problem/D", "Normal", false, ["DP", "Knapsack"], "dp[i][j][l] -> maximum amount of twos we can collect by checking first i numbers, taking j of them with total power of five equal to l"), + ], + pathsGrid: [ + new Problem("LC", "Longest Common Subsequence", "https://leetcode.com/problems/longest-common-subsequence/", "Easy", true, ["DP"], "dp[first i characters in first string][first j characters in second string] -> longest common subsequence, transition if s[i] = t[j] for strings s and t"), + new Problem("HR", "Edit Distance", "contests/cse-830-homework-3/challenges/edit-distance", "Easy", true, ["DP"], "dp[first i characters in first string][first j characters in second string] -> edit distance"), + 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 ... (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/", "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"), + ], +} + +**Dynamic Programming** (DP) is an important algorithmic technique in Competitive Programming from the gold division to competitions like the International Olympiad of Informatics. By breaking down the full task into sub-problems, DP avoids the redundant computations of brute force solutions. + +Although it is not too difficult to grasp the general ideas behind DP, the technique can be used in a diverse range of problems and is a must-know idea for competitors in the USACO gold division. + +<IncompleteSection> + +</IncompleteSection> + +## Introductory Resources + +<Resources> + <Resource source="CPH" title="7 - DP" starred>Great introduction that covers most classical problems. Mentions memoization.</Resource> + <Resource source="TC" title="DP from Novice to Advanced" url="dynamic-programming-from-novice-to-advanced">great for all skill levels</Resource> + <Resource source="CPC" title="6 - DP" url="06_dynamic_programming">examples with nonclassical problems</Resource> + <Resource source="HR" title="DP" url="https://www.hackerrank.com/topics/dynamic-programming">also covers many classical problems</Resource> + <Resource source="PAPS" title="9 - DP">starts with DAGs, which are covered in "Topological Sort"</Resource> +</Resources> + +<Info title="Pro Tip"> + +Sometimes it's a good idea to write a slower polynomial-time solution and then optimize it to the desired complexity (say, write $O(N^2)$ first and then speed it up to $O(N)$). + +</Info> + +## Introductory USACO Problems + +These are easier USACO problems which use DP, and don't require many optimizations or complex states. + +<Problems problems={problems.usacoEasy} /> + +## Classical Problems + +Get familiar with some classical problems, or Dynamic Programming problems which are well known. <Asterisk> However, classical doesn't necessarily mean common. Since so many competitors know about these problems, problemsetters rarely set direct applications of them. </Asterisk> Solutions for most of these problems can be found in Chapter 7 of CPH. + +### Knapsack + +Common variations on Knapsack, followed by more challenging problems which feature variations on the state and additional algorithms. + +<Problems problems={problems.knapsack} /> + +### Paths in a Grid (and related) + +Interesting applications of "number of paths on a grid," some of which don't directly present a grid in the problem, but can be modelled as one. <Asterisk> Such as Longest Common Subsequence. </Asterisk> + +<Problems problems={problems.pathsGrid} /> + +### Longest Increasing Subsequence + +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={problems.lis} /> + +## Harder USACO Problems + +Finish with some more challenging Dynamic Programming problems! Some of these can be very difficult, so skip around if you want. + +<Problems problems={problems.usacoPast} /> + +## Other DP Problemsets + +Dynamic Programming is a huge topic, so if you want more practice, here are some other problemsets that we found interesting. + +<Resources> + <Resource source="AC" title="DP Contest" url="https://atcoder.jp/contests/dp/tasks" starred>very good!</Resource> + <Resource source="CSES" title="DP Section" url="https://cses.fi/problemset/list/" starred>also very good!</Resource> + <Resource source="CF" title="DP List" url="blog/entry/325">misc probs, a lot of which you don't need to know at this level</Resource> +</Resources> + diff --git a/content/4_Gold/DP_Trees.mdx b/content/4_Gold/DP_Trees.mdx new file mode 100644 index 0000000..7ff15cf --- /dev/null +++ b/content/4_Gold/DP_Trees.mdx @@ -0,0 +1,184 @@ +--- +id: dp-trees +title: "Dynamic Programming on Trees" +author: Michael Cao +prerequisites: + - dfs + - intro-dp +description: "Using subtrees as subproblems." +frequency: 2 +--- +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Tree Matching", "1130", "Easy", false, ["DP"], ""), + ], + allRoots: [ + new Problem("AC", "Subtree", "https://atcoder.jp/contests/dp/tasks/dp_v", "Normal", false, [], ""), + ], + usaco: [ + 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"), + new Problem("CSES", "Creating Offices", "1752", "Hard", false, ["Greedy"], "equivalent to BOI - Cat in a Tree"), + new Problem("Plat", "Cow At Large", "793", "Hard", false, [], "This is not too hard to fakesolve. First write an (optimized?) O(N^2) DP to pass test cases 1-6. This won't work for test cases 7-11, but in these test cases all trees have at most 20 leaves. Therefore it suffices to compress tree edges (repeatedly remove vertices of degree 2) and run the same solution. For a legit DP solution, see Eric Zhang's comment here: https://codeforces.com/blog/entry/57170?#comment-410179"), + ], +} + +<Problems problems={problems.sample} /> + +## Tutorial + +<Resources> + <Resource source="CF" title="DP on Trees" url="blog/entry/20935"> </Resource> + <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={problems.allRoots} /> + +(dfs twice) + +<Spoiler title="Solution"> + +<LanguageSection> + +<CPPSection> + +```cpp +template<int SZ> struct SubtreeDP { + int par[SZ]; vi adj[SZ]; + void ae(int a, int b) { adj[a].pb(b), adj[b].pb(a); } + struct T { + mi v = 1; + T& operator+=(const T& b) { v *= b.v; return *this; } + void tran() { ++v; } + }; + T up[SZ], down[SZ]; + void dfs(int x) { + trav(t,adj[x]) if (t != par[x]) { + par[t] = x; dfs(t); + down[x] += down[t]; + } + down[x].tran(); + } + void dfs2(int x) { + { + T pre = up[x]; + F0R(i,sz(adj[x])) { + int c = adj[x][i]; if (c == par[x]) continue; + up[c] += pre; pre += down[c]; + } + } + { + T pre; + R0F(i,sz(adj[x])) { + int c = adj[x][i]; if (c == par[x]) continue; + up[c] += pre; pre += down[c]; + } + } + F0R(i,sz(adj[x])) { + int c = adj[x][i]; if (c == par[x]) continue; + up[c].tran(); dfs2(c); + } + } + T getSub(int x, int y) { return par[x] == y ? down[x] : up[y]; } + void init(int n) { + par[1] = 0; dfs(1); dfs2(1); + FOR(i,1,n+1) { + T p = T(); trav(t,adj[i]) p += getSub(t,i); + ps(p.v); + } +}; + +int main() { + setIO(); int n; re(n,MOD); + SubtreeDP<MX> S; + F0R(i,n-1) { + int a,b; re(a,b); + S.ae(a,b); + } + S.init(n); +} +``` + +</CPPSection> + +</LanguageSection> + + +</Spoiler> + +## Combining Subtrees + +(show why complexity is $O(N^2)$) + +## Problems + +<Info title="Pro Tip"> + +Don't just dive into trying to figure out a DP state and transitions -- make some observations if you don't see any obvious DP solution! Also, sometimes a greedy strategy suffices. + +</Info> + +<Problems problems={problems.usaco} /> + +<Spoiler title="Ostap & Tree"> + +Solution described by editorial is terrible, but we can do better! This runs in $O(NK)$ time. (proof?) + +```cpp +vmi yes[101], no[101]; +int n,k; +vi adj[MX]; + +void dfs(int x, int y) { + yes[x] = no[x] = {1}; // black, not black + // dist of closest good vertex + // or dist of farthest bad vertex + auto ad = [](vmi& a, int b, mi c) { + while (sz(a) <= b) a.pb(0); + a[b] += c; + }; + trav(t,adj[x]) if (t != y) { + dfs(t,x); + yes[t].insert(begin(yes[t]),0); + no[t].insert(begin(no[t]),0); + if (sz(no[t]) > k+1) no[t].pop_back(); + vmi YES, NO; + F0R(i,sz(yes[x])) F0R(j,sz(yes[t])) ad(YES,min(i,j),yes[x][i]*yes[t][j]); + F0R(i,sz(no[x])) F0R(j,sz(no[t])) ad(NO,max(i,j),no[x][i]*no[t][j]); + auto yesNo = [&](vmi good, vmi bad) { + F0R(i,sz(good)) F0R(j,sz(bad)) { + if (i+j <= k) ad(YES,i,good[i]*bad[j]); + else ad(NO,j,good[i]*bad[j]); + } + }; + yesNo(yes[x],no[t]); yesNo(yes[t],no[x]); + swap(yes[x],YES); swap(no[x],NO); + } +} + +int main() { + setIO(); re(n,k); + F0R(i,n-1) { + int a,b; re(a,b); + adj[a].pb(b), adj[b].pb(a); + } + dfs(1,0); + mi ans = 0; + trav(t,yes[1]) ans += t; + ps(ans); + // you should actually read the stuff at the bottom +} +``` + +</Spoiler> diff --git a/content/4_Gold/DSU.mdx b/content/4_Gold/DSU.mdx new file mode 100644 index 0000000..d7390ad --- /dev/null +++ b/content/4_Gold/DSU.mdx @@ -0,0 +1,103 @@ +--- +id: dsu +title: "Disjoint Set Union" +author: Benjamin Qi, Michael Cao +prerequisites: + - 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 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("YS", "Union Find", "unionfind", "Intro|Easy", false, []), + ], + 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."), + ], +}; + +<Problems problems={problems.sample} /> + +## Resources + +<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="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> + +Note: You may prefer to use this in place of DFS for computing connected components. + +<Optional title="DSU Complexity Proofs"> + + - [$\log^* n$](https://en.wikipedia.org/wiki/Proof_of_O(log*n)\_time_complexity\_of_union%E2%80%93find) + - [$\alpha (m,n)$](https://dl.acm.org/doi/pdf/10.1145/321879.321884) + +</Optional> + +## Implementations + +<LanguageSection> + +<CPPSection> + +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. + +<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> + +<JavaSection> + +This implementation assumes that `p` is an array that starts such that `p[i] = i` for every $0 <= i <= n$. It uses path compression only. + +```java +//finds the "representative" node in a's component +int find(int a) { + if (p[a] == a) + return a; + else + return p[a] = find(p[a]); +} + +//returns whether the merge changed connectivity +boolean merge(int a, int b) { + int A = find(a); + int B = find(b); + if (A == B) + return false; + p[a] = p[b] = p[A] = p[B]; + return true; +} +``` + +</JavaSection> + +</LanguageSection> + +## Problems + +<Problems problems={problems.general} /> diff --git a/content/4_Gold/Faster_Hash.mdx b/content/4_Gold/Faster_Hash.mdx new file mode 100644 index 0000000..3e64bc1 --- /dev/null +++ b/content/4_Gold/Faster_Hash.mdx @@ -0,0 +1,151 @@ +--- +id: faster-hashmap +title: A Faster Hash Table in C++ +author: Benjamin Qi +description: "Introduces gp_hash_table." +frequency: 1 +prerequisites: + - unordered +--- + +import { Problem } from "../models"; + +export const problems = { + three: [ + new Problem("Gold", "3SUM", "994", "Normal", false, [], ""), + ], + four: [ + new Problem("CSES", "Sum of Four Values", "1642", "Normal", false, [], ""), + ], +}; + +<Resources> + <Resource source="CF" url="https://codeforces.com/blog/entry/60737" title="Chilli - Order of magnitude faster hash tables" starred>Introduces gp_hash_table</Resource> + <Resource source="GCC" url="https://gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/gp_hash_table.html#Resize_Policy566860465" title="gp_hash_table Interface">documentation</Resource> + <Resource source="Benq (from KACTL)" title="HashMap" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/STL%20(5)/HashMap.h" starred> </Resource> +</Resources> + +<br /> + +Read / writes are much faster than `unordered_map`. Its actual size is always a power of 2. The documentation is rather confusing, so I'll just summarize the most useful functions here. + +```cpp +#include <ext/pb_ds/assoc_container.hpp> +using namespace __gnu_pbds; +``` + +## Unordered Set + +`gp_hash_table<K,null_type>` functions similarly to `unordered_set<K>`. + +## Hacking + +`gp_hash_table` is also vulnerable to hacking. + +(example of hash function that fails) + +To avoid this, we can use one of the custom hash functions mentioned in the Bronze module. + +```cpp +template<class K,class V> using ht = gp_hash_table<K,V,chash>; +``` + +## Resizing + +Unordered map has [`reserve`](http://www.cplusplus.com/reference/unordered_map/unordered_map/reserve/). Calling this function before inserting any elements can result in a constant factor speedup. + +We can modify the declaration of `gp_hash_table` so that it supports the `resize` function, which operates similarly. + +```cpp +template<class K,class V> using ht = gp_hash_table< + K, + null_type, + hash<K>, + equal_to<K>, + direct_mask_range_hashing<>, + linear_probe_fn<>, + hash_standard_resize_policy< + hash_exponential_size_policy<>, + hash_load_check_resize_trigger<>, + true + > +>; +``` + +These are the same template arguments as the default `gp_hash_table`, except `false` has been changed to `true`. This modification allows us to change the actual size of the hash table. + +```cpp +int main() { + ht<int,null_type> g; g.resize(5); + cout << g.get_actual_size() << "\n"; // 8 + cout << g.size() << "\n"; // 0 +} +``` + +When calling `g.resize(x)`, `x` is rounded up to the nearest power of 2. Then the actual size of `g` is changed to be equal to `x` (unless `x < g.size()`, in which case an error is thrown). + +<Resources> + <Resource source="GCC" url="https://gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/hash_standard_resize_policy.html" title="Resize Policy">documentation</Resource> +</Resources> + +Furthermore, if we construct `g` with the following arguments: + +```cpp +ht<int,null_type> g({},{},{},{},{1<<16}); +``` + +then the actual size of `g` is always at least `1<<16` (regardless of calls to `resize`). The last argument **must** be a power of 2 (or else errors will be thrown). + +### Solving ThreeSum + +<Problems problems={problems.three} /> + +You're supposed to use array since values are small :| + +```cpp +#include <bits/stdc++.h> +using namespace std; + +void setIO(string name) { + ios_base::sync_with_stdio(0); cin.tie(0); + freopen((name+".in").c_str(),"r",stdin); + freopen((name+".out").c_str(),"w",stdout); +} + + +#include <ext/pb_ds/assoc_container.hpp> +using namespace __gnu_pbds; + +int N,Q; +long long ans[5000][5000]; +vector<int> A; + +int main() { + setIO("threesum"); + cin >> N >> Q; + A.resize(N); for (int i = 0; i < N; ++i) cin >> A[i]; + for (int i = 0; i < N; ++i) { + gp_hash_table<int,int> g({},{},{},{},{1<<13}); + // initialize with certain capacity, must be power of 2 + for (int j = i+1; j < N; ++j) { + int res = -A[i]-A[j]; + auto it = g.find(res); + if (it != end(g)) ans[i][j] = it->second; + g[A[j]] ++; + } + } + for (int i = N-1; i >= 0; --i) for (int j = i+1; j < N; ++j) + ans[i][j] += ans[i+1][j]+ans[i][j-1]-ans[i+1][j-1]; + for (int i = 0; i < Q; ++i) { + int a,b; cin >> a >> b; + cout << ans[a-1][b-1] << "\n"; + } + // you should actually read the stuff at the bottom +} +``` + +<IncompleteSection /> + +## Problems + +<Problems problems={problems.four} />
\ No newline at end of file diff --git a/content/4_Gold/Intro_NT.mdx b/content/4_Gold/Intro_NT.mdx new file mode 100644 index 0000000..f077c25 --- /dev/null +++ b/content/4_Gold/Intro_NT.mdx @@ -0,0 +1,233 @@ +--- +id: intro-nt +title: "Introductory Number Theory" +author: Darren Yao, Michael Cao +prerequisites: + - intro-dp + - binary representation +description: Introduces divisibility and modular arithmetic. +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + 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$."), + ], +}; + +<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.1, 16.2, 16.4 - Number Theory"></Resource> +</Resources> + +<!-- <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> + --> + +## Prime Factorization + +<Problems problems={problems.sample} /> + +A number $a$ is called a **divisor** or a **factor** of a number $b$ if $b$ is divisible by $a$, which means that there exists some integer $k$ such that $b = ka$. Conventionally, $1$ and $n$ are considered divisors of $n$. A number $n > 1$ is **prime** if its only divisors are $1$ and $n$. Numbers greater than \(1\) that are not prime are **composite**. + +Every number has a unique **prime factorization**: a way of decomposing it into a product of primes, as follows: +$$ +n = {p_1}^{a_1} {p_2}^{a_2} \cdots {p_k}^{a_k} +$$ +where the $p_i$ are distinct primes and the $a_i$ are positive integers. + +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$. + +![Example](factoralgorithm2.png) + +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 + +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} + a & b = 0 \\ + \gcd(b, a \bmod b) & b \neq 0 \\ +\end{cases} +$$ + +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; + return gcd(b, 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)}=\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, + +$$ +\gcd(a_1, a_2, a_3, a_4) = \gcd(a_1, \gcd(a_2, \gcd(a_3, a_4))). +$$ + +## Modular Arithmetic + +<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 +$$ + +$$ +(a-b) \bmod m = (a \bmod m - b \bmod m) \bmod m +$$ + +$$ +(a \cdot b) \pmod{m} = ((a \bmod m) \cdot (b \bmod m)) \bmod m +$$ + +$$ +a^b \bmod {m} = (a \bmod m)^b \bmod m +$$ + +### 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 code to compute $x ^ n \pmod m$: + +<LanguageSection> + +<CPPSection> + +```cpp +long long binpow(long long x, long long n, long long m) { + x %= m; //note: m*m must be less than 2^63-1 to avoid long long overflow + long long res = 1; + while (n > 0) { + if (n % 2 == 1) //if n is odd + res = res * x % m; + x = x * x % m; + n /= 2; //divide by two + } + return res; +} +``` + +</CPPSection> + +</LanguageSection> + +### Modular Inverse + +We'll only consider **prime** moduli here. Division can be performed using modular inverses. + +<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**. + +<IncompleteSection> + +explain theorem? + +</IncompleteSection> + +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$. + +Because it takes $\mathcal{O}(\log \mathrm{MOD})$ time to compute a modular inverse, frequent use of division inside a loop can significantly increase the running time of a program. If the modular inverse of the same number(s) is/are being used many times, it is a good idea to precalculate it. + +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. + +#### With Extended Euclidean Algorithm + +<Optional> + +See the module in the [Advanced](../adv/extend-euclid) section. + +</Optional> + +## Problems + +<Problems problems={problems.general} /> diff --git a/content/4_Gold/MST.mdx b/content/4_Gold/MST.mdx new file mode 100644 index 0000000..658312e --- /dev/null +++ b/content/4_Gold/MST.mdx @@ -0,0 +1,49 @@ +--- +id: mst +title: "Minimum Spanning Trees" +author: Benjamin Qi +prerequisites: + - sp + - dsu +description: "A subset of the edges of a connected, undirected, edge-weighted graph that connects all the vertices to each other of minimum total weight, where no cycles are allowed." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + standard: [ + new Problem("Kattis", "MST", "minspantree", "Easy", false, ["MST"], ""), + new Problem("CSES", "Road Reparation", "1675", "Easy", false, ["MST"], ""), + ], + general: [ + new Problem("Old Silver", "SuperBull", "531", "Easy", false, ["MST", "Prim"], "Prim's is recommended; Kruskal's algorithm might TLE"), + new Problem("Gold", "Fencedin", "623", "Easy", false, ["MST"], ""), + new Problem("Gold", "Walk", "946", "Normal", false, ["Math", "Prim"], ""), + new Problem("HR", "Spanning Tree Fraction", "https://www.hackerrank.com/contests/w31/challenges/spanning-tree-fraction/problem", "Normal", false, ["MST", "Binary Search"], ""), + new Problem("Plat", "Fencedin", "625", "Hard", false, ["Kruskal"], ""), + ], +}; + +<Problems problems={problems.standard} /> + +## Resources + +<Resources> + <Resource source="CPH" title="15 - Spanning Trees" starred>Kruskal's & Prim's</Resource> + <Resource source="PAPS" title="12.4">Kruskal's</Resource> + <Resource source="cp-algo" title="Prim's" url="graph/mst_prim.html"></Resource> + <Resource source="cp-algo" title="Kruskal's" url="graph/mst_kruskal.html"></Resource> + <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={problems.general} /> diff --git a/content/4_Gold/PURS.mdx b/content/4_Gold/PURS.mdx new file mode 100644 index 0000000..96bc810 --- /dev/null +++ b/content/4_Gold/PURS.mdx @@ -0,0 +1,214 @@ +--- +id: PURS +title: "Point Update Range Sum" +author: Benjamin Qi +prerequisites: + - prefix-sums +description: "Introduces Segment Tree, Binary Indexed Tree, and Order Statistic Tree (C++ only)." +frequency: 3 +--- + +import { Problem } from "../models"; + +export const 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!"), + new Problem("Plat", "Slingshot", "816", "Very Hard", false, ["PURQ"], ""), + ], + 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."), + new Problem("SPOJ", "Inversion Counting", "INVCNT", "Easy", false, ["PURS"]), + ], + practice: [ + new Problem("CSES", "List Removals", "1749", "Easy", false, undefined, "easy with indexed set"), + new Problem("CSES", "Salary Queries", "1144", "Easy", false, undefined, "easy with indexed set"), + new Problem("CSES", "Range Update Queries", "1651", "Easy", false, undefined, "not harder than point update range query"), + 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("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, ""), + ], + usaco: [ + new Problem("Gold", "Haircut", "1041", "Easy", false, undefined, ""), + new Problem("Gold", "Balanced Photo", "693", "Easy", false, undefined, ""), + new Problem("Gold", "Circle Cross", "719", "Easy", false, undefined, ""), + new Problem("Gold", "Sleepy Cow Sort", "898", "Easy", false, undefined, "All USACO problems (aside from some special exceptions) have only one possible output."), + new Problem("Plat", "Mincross", "720", "Easy", false, undefined, ""), + new Problem("Silver", "Out of Sorts", "834", "Normal", false, undefined, "aka Sorting Steps: https://csacademy.com/contest/round-42/task/sorting-steps/ - Of course, this doesn't require anything other than sorting but fast range sum queries may make this easier."), + new Problem("Gold", "Out of Sorts", "837", "Hard", false, undefined, ""), + new Problem("Old Gold", "Cow Hopscotch", "532", "Hard", false, [], ""), + new Problem("Plat", "Out of Sorts", "840", "Hard", false, [], ""), + ], +}; + +<Problems problems={problems.sample} /> + +<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 some consecutive subarray. + +Both **segment trees** and **binary indexed trees** can accomplish this. + +## Segment Tree + +<Problems problems={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 + +<Resources> + <Resource source="CSA" title="Fenwick Trees" url="fenwick_trees" starred>interactive</Resource> + <Resource source="CPH" title="9.2, 9.4 - Binary Indexed Tree" starred>similar to above</Resource> + <Resource source="cp-algo" title="Fenwick Tree" url="data_structures/fenwick.html">also similar to above</Resource> + <Resource source="TC" title="Binary Indexed Trees" url="binary-indexed-trees"></Resource> +</Resources> + +### 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> + +</CPPSection> + +</LanguageSection> + + +## Finding $k$-th Element + +Suppose that we want a data structure that supports all the operations as a `set` in C++ in addition to the following: + + - `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). + +### Order Statistic Tree + +Luckily, such a built-in data structure already exists in C++. + +<Resources> + <Resource source="CF" url="blog/entry/11080" title="adamant - Policy Based Data Structures" starred> </Resource> + <Resource source="Benq" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/STL%20(5)/IndexedSet.h" title="Indexed Set"></Resource> +</Resources> + +Using this, we can solve "Inversion Counting" in just a few lines (with template). + +<!-- <Spoiler title="INVCNT with Indexed Set"> --> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +#include <ext/pb_ds/tree_policy.hpp> +#include <ext/pb_ds/assoc_container.hpp> +using namespace __gnu_pbds; +template <class T> using Tree = tree<T, null_type, less<T>, + rb_tree_tag, tree_order_statistics_node_update>; + +int main() { + int T; cin >> T; + for (int i = 0; i < T; ++i) { + int n; cin >> n; + Tree<int> TS; long long numInv = 0; + for (int j = 0; j < n; ++j) { + int x; cin >> x; + numInv += j-TS.order_of_key(x); // gives # elements before it > x + TS.insert(x); + } + cout << numInv << "\n"; + } +} +``` + +<!-- </Spoiler> --> + +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. + +### 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> +</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={problems.practice} /> + +## USACO Problems + +Haircut, Balanced Photo, and Circle Cross are just variations on inversion counting. + +<Problems problems={problems.usaco} /> diff --git a/content/4_Gold/Queues.mdx b/content/4_Gold/Queues.mdx new file mode 100644 index 0000000..66fe38a --- /dev/null +++ b/content/4_Gold/Queues.mdx @@ -0,0 +1,135 @@ +--- +id: queues +title: Queues & Deques +author: Darren Yao +description: "Data structures that allow insertion and deletion at both ends." +prerequisites: + - intro-ds + - sliding +--- + +import { Problem } from "../models"; + +export const problems = { + ys: [ + new Problem("YS","Persistent Queue","persistent_queue","Normal",false,["DFS"],""), + new Problem("YS","Queue Composite","queue_operate_all_composite","Hard",true,[],""), + ], + sam: [ + new Problem("LC", "Sliding Window Maximum", "sliding-window-maximum", "Easy", false, [], ""), + ], +}; + +<Resources> + <Resource source="CPH" title="4.5 - Queues, Deques"></Resource> + <Resource source="PAPS" title="3.2, 6.3 - Queues"></Resource> +</Resources> + +## Queues + +A queue is a First In First Out (FIFO) data structure that supports three operations, all in $O(1)$ time. + +<LanguageSection> + +<CPPSection> + +### [C++](http://www.cplusplus.com/reference/queue/queue/) + + - `push`: insertion at the back of the queue + - `pop`: deletion from the front of the queue + - `front`: which retrieves the element at the front without removing it. + +```cpp +queue<int> q; +q.push(1); // [1] +q.push(3); // [3, 1] +q.push(4); // [4, 3, 1] +q.pop(); // [4, 3] +cout << q.front() << endl; // 3 +``` + +</CPPSection> + +<JavaSection> + +### Java + + - `add`: insertion at the back of the queue + - `poll`: deletion from the front of the queue + - `peek`: which retrieves the element at the front without removing it + +Java doesn't actually have a `Queue` class; it's only an interface. The most commonly used implementation is the `LinkedList`, declared as follows: + +```java +Queue<Integer> q = new LinkedList<Integer>(); +q.add(1); // [1] +q.add(3); // [3, 1] +q.add(4); // [4, 3, 1] +q.poll(); // [4, 3] +System.out.println(q.peek()); // 3 +``` + +</JavaSection> + +</LanguageSection> + +## Deques + +A **deque** (usually pronounced "deck") stands for double ended queue and is a combination of a stack and a queue, in that it supports $O(1)$ insertions and deletions from both the front and the back of the deque. Not very common in Bronze / Silver. + +<LanguageSection> + +<CPPSection> + +### [C++](http://www.cplusplus.com/reference/deque/deque/) + +The four methods for adding and removing are `push_back`, `pop_back`, `push_front`, and `pop_front`. + +```cpp +deque<int> d; +d.push_front(3); // [3] +d.push_front(4); // [4, 3] +d.push_back(7); // [4, 3, 7] +d.pop_front(); // [3, 7] +d.push_front(1); // [1, 3, 7] +d.pop_back(); // [1, 3] +``` + +<IncompleteSection> +(important: you can also access like array ...) +</IncompleteSection> + +</CPPSection> + +<JavaSection> + +### Java + +In Java, the deque class is called `ArrayDeque`. The four methods for adding and removing are `addFirst` , `removeFirst`, `addLast`, and `removeLast`. + +```java +ArrayDeque<Integer> deque = new ArrayDeque<Integer>(); +deque.addFirst(3); // [3] +deque.addFirst(4); // [4, 3] +deque.addLast(7); // [4, 3, 7] +deque.removeFirst(); // [3, 7] +deque.addFirst(1); // [1, 3, 7] +deque.removeLast(); // [1, 3] +``` + +</JavaSection> + +</LanguageSection> + +## Sliding Window Minimum in $O(N)$ + +<Problems problems={problems.sam} /> + +<Resources> + <Resource source="CPH" title="8.3 - Sliding Window Minimum"></Resource> + <Resource source="cp-algo" title="Minimum stack / Minimum queue" url="data_structures/stack_queue_modification.html" starred>Mentions two ways to solve this (both are important)!</Resource> +</Resources> + +## Problems + +<Problems problems={problems.ys} />
\ No newline at end of file diff --git a/content/4_Gold/SP.mdx b/content/4_Gold/SP.mdx new file mode 100644 index 0000000..e714864 --- /dev/null +++ b/content/4_Gold/SP.mdx @@ -0,0 +1,117 @@ +--- +id: sp +title: "Shortest Paths with Non-Negative Edge Weights" +author: Benjamin Qi +prerequisites: + - bfs + - intro-ordered +description: "Introduces Dijkstra's Algorithm for a single source shortest path as well as Floyd-Warshall for All-Pairs Shortest Path." +frequency: 3 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Shortest Routes I", "1671", "Easy", false, ["SP"], "equivalent to [Kattis SSSP Non-Negative](https://open.kattis.com/problems/shortestpath1)"), + ], + dijk: [ + new Problem("CSES", "Flight Discount", "1195", "Easy", false, ["SP"], "one edge modified"), + 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", "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("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"], ""), + ], + apsp: [ + new Problem("Gold", "Moortal Cowmbat", "971", "Hard", false, ["APSP", "DP"], ""), + ], +}; + +## Single-Source Shortest Path + +<Problems problems={problems.sample} /> + +### Tutorial + +Use *Dijkstra's Algorithm*. + +<Resources> + <Resource source="CPH" title="13.2 - Dijkstra" starred>code</Resource> + <Resource source="cp-algo" title="Dijkstra (Dense Graphs)" url="graph/dijkstra_dense.html"></Resource> + <Resource source="cp-algo" title="Dijkstra (Sparse Graphs)" url="graph/dijkstra_sparse.html"></Resource> + <Resource source="CPC" title="8 - Graphs 2" url="08_graphs_2"></Resource> +</Resources> + + +### Implementation + +#### Part 1: $O(N^2)$ + +The USACO training pages present a $O(N^2)$ version, although this is rarely used nowadays. (but describe anyways?) + +<IncompleteSection /> + +#### Part 2: $O(M\log N)$ + +<Resources> + <Resource source="Benq" title="Dijkstra" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/graphs%20(12)/Basics/Dijkstra%20(7.3).h"> </Resource> +</Resources> + +<IncompleteSection /> + +(comments in code?) + +<Optional title="Faster Dijkstra"> + +Can be done in $O(M+N\log N)$ with [Fibonacci heap](https://en.wikipedia.org/wiki/Fibonacci_heap). + +</Optional> + +### Problems + +<Problems problems={problems.dijk} /> + +## All Pairs Shortest Path (APSP) + +<Problems problems={problems.apspSam} /> + +Use the *Floyd-Warshall* algorithm. + +### Tutorial + +<Resources> + <Resource source="CPH" title="13.3 - Floyd-Warshall" starred>example calculation, code</Resource> + <Resource source="PAPS" title="12.3.3"></Resource> + <Resource source="cp-algo" title="Floyd-Warshall" url="graph/all-pair-shortest-path-floyd-warshall.html"></Resource> +</Resources> + +<Optional title="Incorrect Floyd-Warshall"> + +[Paper](https://arxiv.org/pdf/1904.01210.pdf) + +> A common mistake in implementing the Floyd–Warshall algorithm is to +misorder the triply nested loops (The correct order is `KIJ`). The incorrect +`IJK` and `IKJ` algorithms do not give correct solutions for some instance. However, we can prove that if these are repeated three times, we obtain the correct solutions. +> +> It would be emphasized that these fixes (repeating incorrect algorithms three +times) have the same time complexity as the correct Floyd–Warshall algorithm +up to constant factors. Therefore, our results suggest that, if one is confused by +the order of the triply nested loops, one can repeat the procedure three times +just to be safe. + +</Optional> + +### Problems + +<Problems problems={problems.apsp} /> + +(more?) + +<IncompleteSection />
\ No newline at end of file diff --git a/content/5_Gold/SRQ.mdx b/content/4_Gold/SRQ.mdx index d027be1..0d61643 100644 --- a/content/5_Gold/SRQ.mdx +++ b/content/4_Gold/SRQ.mdx @@ -2,17 +2,17 @@ id: SRQ title: "Static Range Queries" author: Benjamin Qi -description: Range queries for any associative operation over a static array. +description: "Range queries for any associative operation over a static array." frequency: 1 --- + import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { rmqSample: [ - new Problem("YS", "Static RMQ", "staticrmq", "Easy", false, []), + new Problem("YS", "Static RMQ", "staticrmq", "Easy", false, [], "equivalent to [CSES Range Minimum Queries I](https://cses.fi/problemset/task/1647)"), ], - diviSample: [ + diviSample: [ new Problem("ojuz", "JOI Secret", "JOI14_secret", "Easy", false, [], ""), ], general: [ @@ -20,37 +20,42 @@ export const metadata = { new Problem("DMOJ", "Continued Fractions", "dmopc19c7p4", "Hard", false, [], ""), new Problem("Plat", "Non-Decreasing Subsequences", "997", "Very Hard", false, [], ""), ], - } }; -Given a static array $A[1],A[2],\ldots,A[N]$, you want to answer queries in the form $A[l]\ominus A[l+1]\ominus \cdots \ominus A[r]$ where $\ominus$ denotes any associative operation. - -With $O(N\log N)$ time preprocessing, we can get $O(1)$ queries. +Given a static array $A[1],A[2],\ldots,A[N]$, you want to answer queries in the form +$$ +A[l]\ominus A[l+1]\ominus \cdots \ominus A[r] +$$ +where $\ominus$ denotes any associative operation. With $O(N\log N)$ time preprocessing, we can get $O(1)$ time queries! ## [Range Minimum Query](https://en.wikipedia.org/wiki/Range_minimum_query) -<problems-list problems={metadata.problems.rmqSample} /> - First we'll consider the special case when $\ominus$ denotes `min`. -- [CSES Range Minimum Queries I](https://cses.fi/problemset/task/1647) +<Problems problems={problems.rmqSample} /> -### Tutorial +### Resources - - CPH 9.1 - - [PAPS 11.2.2](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [cp-algo RMQ](https://cp-algorithms.com/sequences/rmq.html) - - [cp-algo Sparse Table](https://cp-algorithms.com/data_structures/sparse-table.html) +<Resources> + <Resource source="CPH" title="9.1 - Minimum Queries" starred>diagrams</Resource> + <Resource source="PAPS" title="11.2.2 - Sparse Tables" starred>code</Resource> + <Resource source="cp-algo" title="RMQ" url="sequences/rmq.html"></Resource> + <Resource source="cp-algo" title="Sparse Table" url="data_structures/sparse-table.html"></Resource> +</Resources> -<optional-content title="Preprocessing in O(N) Time"> +<Optional title="Faster Preprocessing"> - - [CF: $O(1)$ Query RMQ with $O(N)$ build](https://codeforces.com/blog/entry/78931) +[CF: $O(1)$ Query RMQ with $O(N)$ build](https://codeforces.com/blog/entry/78931) -</optional-content> +</Optional> + +### Implementation + +<IncompleteSection /> ## Divide & Conquer -<problems-list problems={metadata.problems.diviSample} /> +<Problems problems={problems.diviSample} /> **Divide & conquer** can refer to many different techniques. In this case, we use it to answer $Q$ queries offline in $O((N+Q)\log N)$ time. @@ -66,15 +71,19 @@ for each $M< r\le R$. Then the answer for all queries satisfying $l\le M< r$ is Actually, this can be adjusted to answer queries online in $O(1)$ time each. See my implementation [here](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/Static%20Range%20Queries%20(9.1)/RangeQuery.h). -<optional-content title="Faster Preprocessing"> +<Optional title="Faster Preprocessing"> A data structure known as **sqrt-tree** can speed up preprocessing time to $O(N\log \log N)$. - [CF Blog Pt 1](http://codeforces.com/blog/entry/57046) - [CF Blog Pt 2](http://codeforces.com/blog/entry/59092) -</optional-content> +</Optional> + +### Implementation + +<IncompleteSection /> ## Problems -<problems-list problems={metadata.problems.general} />
\ No newline at end of file +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/4_Gold/Sliding.mdx b/content/4_Gold/Sliding.mdx new file mode 100644 index 0000000..275f90d --- /dev/null +++ b/content/4_Gold/Sliding.mdx @@ -0,0 +1,73 @@ +--- +id: sliding +title: "Sliding Window" +author: Darren Yao, Benjamin Qi +prerequisites: + - intro-ordered + - 2P +description: "Maintaining data over consecutive subarrays." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + constantSam: [ + new Problem("LC", "Sliding Window Maximum", "sliding-window-maximum", "Easy", false, [], ""), + ], + constant: [ + new Problem("CSES", "Max Subarray Sum II", "1644", "Normal", false, ["prefix-sums"], ""), + new Problem("CSES", "Sliding Median", "1076", "Normal", false, [], ""), + new Problem("CSES", "Sliding Cost", "1077", "Hard", false, [], ""), + ], + sam: [ + new Problem("CSES", "Playlist", "1141", "Easy", false, ["2P"], ""), + ], + general: [ + new Problem("CF", "K-Good Segment", "problemset/problem/616/D", "Easy", false, []), + new Problem("Gold", "Haybale Feast", "767", "Easy", false, ["Set", "Sliding Window"]), + new Problem("Plat", "Fort Moo", "600", "Hard", false, ["Sliding Window"]), + ] +}; + +## Sliding Window + +<Problems problems={problems.constantSam} /> + +From CPH: + +> A sliding window is a constant-size subarray that moves from left to right +through the array. + +For each position of the window, we want to compute some information. For example, we could store an ordered set of integers representing the integers inside the window. If the window currently spans the range $i \dots j$, we observe that moving the range forward to $i+1 \dots j+1$ only removes $a_i$ and adds $a_{j+1}$ to the window. We can support these two operations and query for the minimum / maximum in the set in $O(\log N)$. + +<!-- To compute the sum in the range, instead of using a set, we can store a variable $s$ representing the sum. As we move the window forward, we update $s$ by subtracting $a_i$ from $s$ and adding $a_{j+1}$ to $s$. + --> + +<Resources> + <Resource source="GFG" title="Window Sliding Technique" url="window-sliding-technique"> </Resource> +</Resources> + +### Implementation + +<IncompleteSection /> + +### Problems + +<Problems problems={problems.constant} /> + +## With Two Pointers + +In general, it is not required for the subarray to have constant size as long as both the left and right endpoints of the subarray only move to the right. + +<Problems problems={problems.sam} /> + +### Solution + +<Resources> + <Resource source="Medium" title="Introduction to Sliding Window Algorithms" url="https://levelup.gitconnected.com/an-introduction-to-sliding-window-algorithms-5533c4fe1cc7"> </Resource> +</Resources> + +### Problems + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/5_Gold/Springboards.mdx b/content/4_Gold/Springboards.mdx index 03f4e03..f96483b 100644 --- a/content/5_Gold/Springboards.mdx +++ b/content/4_Gold/Springboards.mdx @@ -3,24 +3,29 @@ id: springboards title: "Max Suffix Query with Insertions Only" author: Benjamin Qi prerequisites: - - Silver - More with Maps & Sets - - Silver - Amortized Analysis -description: "Solving USACO Gold - Springboards." + - intro-ordered +description: "A solution to USACO Gold - Springboards." frequency: 1 --- import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { + sample: [ + new Problem("Gold", "Springboards", "995", "Hard", false, [], ""), + ], problems: [ + new Problem("Plat", "Nocross", "721", "Hard", false, [], ""), new Problem("CF", "Karen & Cards", "contest/815/problem/D", "Very Hard", false, [], "For each a from p to 1, calculate the number of possible cards with that value of a."), new Problem("CF", "GP of Korea 19 - Interesting Drug", "gym/102059/problem/K", "Very Hard", false, [], "Genfuncs not required but possibly helpful"), ] - } }; -To solve USACO Gold [Springboards](http://www.usaco.org/index.php?page=viewproblem2&cpid=995), we need a data structure that supports operations similar to the following: +<Problems problems={problems.sample} /> + +<br/> + +To solve this problem, we need a data structure that supports operations similar to the following: 1. Add a pair $(a,b)$. 2. For any $x$, query the maximum value of $b$ over all pairs satisfying $a\ge x$. @@ -35,6 +40,10 @@ that if there exist pairs $(a,b)$ and $(c,d)$ in the map such that $a\le c$ and If there are $N$ insertions, then each query takes $O(\log N)$ time and adding a pair takes $O(\log N)$ time amortized. +<LanguageSection> + +<CPPSection> + ```cpp #define f first #define s second @@ -42,17 +51,29 @@ If there are $N$ insertions, then each query takes $O(\log N)$ time and adding a map<int,ll> m; void ins(int a, ll b) { - auto it = m.lb(a); if (it != end(m) && it->s >= b) return; - it = m.insert(it,{a,b}); it->s = b; - while (it != begin(m) && prev(it)->s <= b) m.erase(prev(it)); + auto it = m.lb(a); if (it != end(m) && it->s >= b) return; + it = m.insert(it,{a,b}); it->s = b; + while (it != begin(m) && prev(it)->s <= b) m.erase(prev(it)); } ll query(int x) { auto it = m.lb(x); - return it == end(m) ? 0 : it->s; } + return it == end(m) ? 0 : it->s; } // it = end(m) means that no pair satisfies a >= x ``` +</CPPSection> + +<JavaSection> + + + +</JavaSection> + +</LanguageSection> + ## Problems (easier examples?) -<problems-list problems={metadata.problems.problems} /> +<IncompleteSection /> + +<Problems problems={problems.problems} /> diff --git a/content/4_Gold/Stacks.mdx b/content/4_Gold/Stacks.mdx new file mode 100644 index 0000000..9beae4d --- /dev/null +++ b/content/4_Gold/Stacks.mdx @@ -0,0 +1,122 @@ +--- +id: stacks +title: Stacks +author: Darren Yao +description: "A data structures that only allows insertion and deletion at one end." +prerequisites: + - intro-ds +--- + +import { Problem } from "../models"; + +export const problems = { + nearest: [ + new Problem("CSES", "Nearest Smaller Values", "1645", "Easy", false, [], ""), + ], + 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("Gold", "Modern Art 2","743", "Hard", false, [], ""), + new Problem("Gold", "Dishwashing","922", "Hard", false, [], ""), + ], +}; + +<Resources> + <Resource source="CPH" title="4.5 - Stacks"></Resource> + <Resource source="PAPS" title="3.3, 6.2 - Stacks"></Resource> + <Resource source="CP1" title="2.2 - Data Structures with Built-in Libraries"> </Resource> +</Resources> + +## Stacks + +A stack is a **Last In First Out** (LIFO) data structure that supports three operations, all in $O(1)$ time. Think of it like a real-world stack of papers (or cards). + +<LanguageSection> + +<CPPSection> + +### [C++](http://www.cplusplus.com/reference/stack/stack/) + + - `push`: adds an element to the top of the stack + - `pop`: removes an element from the top of the stack + - `top`: retrieves the element at the top without removing it + +```cpp +stack<int> s; +s.push(1); // [1] +s.push(13); // [1, 13] +s.push(7); // [1, 13, 7] +cout << s.top() << endl; // 7 +s.pop(); // [1, 13] +cout << s.size() << endl; // 2 +``` + +</CPPSection> + +<JavaSection> + +### Java + + - `push`: adds an element to the top of the stack + - `pop`: removes an element from the top of the stack + - `peek`: retrieves the element at the top without removing it + +```java +Stack<Integer> s = new Stack<Integer>(); +s.push(1); // [1] +s.push(13); // [1, 13] +s.push(7); // [1, 13, 7] +System.out.println(s.peek()); // 7 +s.pop(); // [1, 13] +System.out.println(s.size()); // 2 +``` + +</JavaSection> + +</LanguageSection> + + +## Application: Nearest Smaller Element + +<Problems problems={problems.nearest} /> + +Consider the following problem: + +> Given an array, $a$, of $N$ ($1 \le N \le 10^5$) integers, for every index $i$, find the rightmost index $j$ such that $j < i$ and $a_i > a_j$. + +<Resources> + <Resource source="CPH" title="8.2 - Nearest Smaller Element"></Resource> + <Resource source="Medium" title="Monotonic Stack" url="https://medium.com/@vishnuvardhan623/monotonic-stack-e9dcc4fa8c3e"></Resource> +</Resources> + +To solve this, let's store a stack of pairs of `<value, index>` and iterate over the array from left to right. For some index $i$, we will compute $ans_i$, the rightmost index for $i$, as follows: + +- Keep popping the top element off the stack as long as $value \ge a_i$. This is because we know that the pair containing $value$ will never be the solution to any index $j > i$, since $a_i$ is less than or equal to than $value$ and has an index further to the right. +- If $value < a_i$, set $ans[i]$ to $index$, because a stack stores the most recently added values first (or in this case, the rightmost ones), $index$ will contain the rightmost value which is less than $a_i$. Then, pop the top element off the stack, because $index$ can't be the solution for two elements. + +The stack we used is called a **monotonic stack** because we keep popping off the top element of the stack which maintains it's monotonicity (the same property needed for algorithms like binary search) because the elements in the stack are increasing. + +### Implementation + +<IncompleteSection /> + +## Problems + +<Problems problems={problems.general} /> + +<!-- + +(actually go through these and check ...) + +### Stack + + - UVa 00514 - Rails + - UVa 00732 - Anagram by Stack + - UVa 01062 - Containers + +### Queue / Deque + + - UVa 10172 - The Lonesome Cargo + - UVa 10901 - Ferry Loading III + - UVa 11034 - Ferry Loading IV + -->
\ No newline at end of file diff --git a/content/4_Gold/String_Hashing.mdx b/content/4_Gold/String_Hashing.mdx new file mode 100644 index 0000000..31d1468 --- /dev/null +++ b/content/4_Gold/String_Hashing.mdx @@ -0,0 +1,67 @@ +--- +id: string-hashing +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"; + +export const problems = { + ex: [ + new Problem("Gold", "Cownomics", "741", "Easy", false, [], ""), + ], + 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"), + ], + adj: [ + new Problem("CF", "Berland SU Computer Network", "contest/847/problem/L", "Normal", false, [], ""), + ] +}; + +## Tutorial + +<Resources> + <Resource source="CPH" title="26.3 - String Hashing" starred>good intro</Resource> + <Resource source="cp-algo" title="String Hashing" url="string/string-hashing.html" starred>code</Resource> + <Resource source="PAPS" title="14.3 - Hashing" starred>many applications</Resource> + +</Resources> + +### Implementation + +<IncompleteSection /> + +My implementation can be found [here](https://github.com/bqi343/USACO/blob/master/Implementations/content/strings%20(14)/Light/HashRange%20(14.2).h). It uses two bases rather than just one to decrease the probability that two random strings hash to the same value. As mentioned in the articles above, there is no need to calculate modular inverses. + +## Example: Cownomics (Gold) + +<Problems problems={problems.ex} /> + + - Use two pointers; for a fixed $l$, keep extending $r$ to the right until the positions $l\ldots r$ explain spotiness. + - Hashing gives you a way to quickly check whether two substrings of different cow types are equal. So for a single $[l,r]$ pair you can check whether it works in $O(N\log N)$ time (and you only need to check $O(M)$ of these pairs in total). + - Actually, it's possible to pass $O(N^2M)$ (or even slower) solutions. + +<!-- ## Adjacency Lists + +(elaborate) + +<Problems problems={problems.adj} /> + --> + +## Hacking + +<Resources> + <Resource source="CF" title="dacin21 - Anti-Hash Tests" url="blog/entry/60442">On CF educational rounds in particular, make sure to randomize your bases.</Resource> +</Resources> + +## Problems + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/4_Gold/TopoSort.mdx b/content/4_Gold/TopoSort.mdx new file mode 100644 index 0000000..74153f2 --- /dev/null +++ b/content/4_Gold/TopoSort.mdx @@ -0,0 +1,460 @@ +--- +id: toposort +title: "Topological Sort" +author: Benjamin Qi, Michael Cao, Nathan Chen +prerequisites: + - bfs + - intro-dp +description: "An ordering of vertices in a directed acyclic graph that ensures that a node is visited before a node it has a directed edge to." +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Course Schedule", "1679", "Easy", false, []), + ], + dp: [ + new Problem("CSES", "Longest Flight Route", "1680", "Easy", false, []), + ], + general: [ + new Problem("CSES", "Game Routes", "1681", "Easy", false, [], "counting paths on DAG"), + new Problem("Kattis", "Quantum", "https://open.kattis.com/contests/acpc17open/problems/quantumsuperposition", "Easy", false, [], "enumerating paths on DAG"), + new Problem("Gold", "Timeline", "1017", "Easy", false, [], "not explicitly given, but graph is a DAG"), + new Problem("Gold", "Milking Order", "838", "Normal", false, ["TopoSort", "Binary Search"]), + new Problem("CSES", "Course Schedule II", "1681", "Hard", false, [], "equivalent to [Minimal Labels](https://codeforces.com/contest/825/problem/E)"), + ], +}; + +To review, a **directed** graph consists of edges that can only be traversed in one direction. Additionally, a **acyclic** graph defines a graph which does not contain cycles, meaning you are unable to traverse across one or more edges and return to the node you started on. Putting these definitions together, a **directed acyclic** graph, sometimes abbreviated as DAG, is a graph which has edges which can only be traversed in one direction and does not contain cycles. + +## Topological Sort + +<Problems problems={problems.sample} /> + +A [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) of a directed acyclic graph is a linear ordering of its vertices such that for every directed edge $u\to v$ from vertex $u$ to vertex $v$, $u$ comes before $v$ in the ordering. + +## Tutorial + +<Resources> + <Resource source="CSA" title="Topological Sorting" url="topological_sorting" starred>The algorithms to find a topological sort are based on BFS and DFS.</Resource> +</Resources> + +### DFS + +<Resources> + <Resource source="CPH" title="16.1, 16.2 - Topological Sorting">DFS</Resource> + <Resource source="cp-algo" title="Topological Sort" url="graph/topological-sort.html">DFS</Resource> +</Resources> + +(implementation) + +<IncompleteSection /> + +### BFS + +The BFS version, known as [Kahn's Algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm), makes it obvious how to extract the lexicographically minimum topological sort. + +<LanguageSection> + +<CPPSection> + +```cpp +int in_degree[100000]; +vector<int> edge[100000]; + +int N; //number of nodes + +void compute() { + queue<int> q; + for(int i = 0; i < N; i++) { + if(in_degree[i] == 0) { + q.push(i); + } + } + + while(!q.empty()) { + int node = q.front(); + q.pop(); + + for(int next : edge[node]) { + in_degree[next]--; + if(in_degree[next] == 0) q.push(next); + } + + //[put problem-specific processing here] + } + +} +``` + +</CPPSection> + +<JavaSection> + +```java + static int in_degree[]; + static ArrayList<Integer> edge[]; //adjacency list + + static int N; //number of nodes + + static void topological_sort() { + Queue<Integer> q = new ArrayDeque<Integer>(); + for(int i = 0; i < N; i++) { + if(in_degree[i] == 0) { + q.add(i); + } + } + + while(!q.isEmpty()) { + int node = q.poll(); + + for(int next : edge[node]) { + in_degree[next]--; + if(in_degree[next] == 0) q.add(next); + } + + //[put problem-specific processing here] + } + } +``` + +</JavaSection> + +</LanguageSection> + +<IncompleteSection /> + +## Dynamic Programming + +<Resources> + <Resource source="PAPS" title="9.1">Best Path in a DAG</Resource> +</Resources> + +One useful property of directed acyclic graphs is, as the name suggests, that no cycles exist. If we consider each node in the graph as a state, we can perform dynamic programming on the graph if we process the states in an order that guarantees for every edge $u\to v$ that $u$ is processed before $v$. Fortunately, this is the exact definition of a topological sort! + +<Problems problems={problems.dp} /> + +In this task, we must find the longest path in a DAG. + +<Spoiler title="Solution"> + +Let $dp[v]$ denote the length of the longest path ending at the node $v$. Clearly + +$$ +dp[v]=\max_{\text{edge } u\to v \text{ exists}}dp[u]+1, +$$ + +or $1$ if $v$ is node $1$. If we process the states in topological order, it is guaranteed that $dp[u]$ will already have been computed before computing $dp[v]$. + +Note that the implementation of this idea below uses Kahn's algorithm for topological sorting: + +<LanguageSection> + +<CPPSection> + +```cpp +#include <bits/stdc++.h> + +using namespace std; + +int prev_flight[100000]; +int dist[100000]; +int in_degree[100000]; +vector<int> edge[100000]; +vector<int> backEdge[100000]; + +int N, M; + +//Does a topological sort +void compute() { + queue<int> q; + for(int i = 0; i < N; i++) { + if(in_degree[i] == 0) { + q.push(i); + } + } + + while(!q.empty()) { + int node = q.front(); + q.pop(); + + for(int next : edge[node]) { + in_degree[next]--; + if(in_degree[next] == 0) q.push(next); + } + + //The below block computes the DP + int mx = -999999999; + int mx_node = -1; + for(int prev : backEdge[node]) { + if(dist[prev] + 1 > mx) { + mx = dist[prev] + 1; + mx_node = prev; + } + } + + dist[node] = mx; + if(node == 0) dist[node] = 1; + prev_flight[node] = mx_node; + } + +} + +int main() { //See "Intro - Fast I/O" for more information about the first two lines in the main function + ios_base::sync_with_stdio(false); + cin.tie(NULL); + + cin >> N >> M; + + for(int i = 0; i < N; i++) { + prev_flight[i] = -1; + dist[i] = -999999999; + in_degree[i] = 0; + } + + for(int i = 0; i < M; i++) { + int a, b; + cin >> a >> b; + a--, b--; + in_degree[b]++; + edge[a].push_back(b); + + backEdge[b].push_back(a); + } + + compute(); + + stack<int> answer; + int temp = N-1; + + bool contains0 = false; + if(temp == 0) contains0 = true; + + while(temp != -1 && dist[temp] >= 0) { + answer.push(temp); + temp = prev_flight[temp]; + if(temp == 0) contains0 = true; + } + + if(contains0) { + cout << dist[N-1] << "\n"; + while(!answer.empty()) { + cout << answer.top()+1; + answer.pop(); + if(!answer.empty()) cout << " "; + } + cout << "\n"; + } else { + cout << "IMPOSSIBLE\n"; + } + + return 0; +} + ``` + +</CPPSection> + +<JavaSection> + +```java +import java.util.*; +import java.io.*; + +//longest_path +public class Main { + + static int prev_flight[], dist[], in_degree[]; + static ArrayList<Integer> edge[]; + static ArrayList<Integer> backEdge[]; + + static int N, M; + + //does a topological sort + static void compute() { + Queue<Integer> q = new ArrayDeque<Integer>(); + for(int i = 0; i < N; i++) { + if(in_degree[i] == 0) { + q.add(i); + } + } + + while(!q.isEmpty()) { + int node = q.poll(); + + for(int next : edge[node]) { + in_degree[next]--; + if(in_degree[next] == 0) q.add(next); + } + + //The below block computes the DP + int mx = -999999999; + int mx_node = -1; + for(int prev : backEdge[node]) { + if(dist[prev] + 1 > mx) { + mx = dist[prev] + 1; + mx_node = prev; + } + } + + dist[node] = mx; + if(node == 0) dist[node] = 1; + prev_flight[node] = mx_node; + } + } + + public static void main(String[] args) throws Exception { + FastIO sc = new FastIO(System.in); //View "Intro - Fast I/O" for more information about the "FastIO" class + + N = sc.nextInt(); + M = sc.nextInt(); + + prev_flight = new int[N]; + dist = new int[N]; + in_degree = new int[N]; + edge = new ArrayList[N]; + backEdge = new ArrayList[N]; + + for(int i = 0; i < N; i++) { + prev_flight[i] = -1; + dist[i] = -999999999; + in_degree[i] = 0; + edge[i] = new ArrayList<Integer>(); + backEdge[i] = new ArrayList<Integer>(); + } + + for(int i = 0; i < M; i++) { + int a = sc.nextInt(), b = sc.nextInt(); + a--; b--; + in_degree[b]++; + edge[a].add(b); + backEdge[b].add(a); + } + + compute(); + + PrintWriter pw = new PrintWriter(System.out); + + ArrayDeque<Integer> answer = new ArrayDeque<Integer>(); //Acts as a Stack + int temp = N-1; + + boolean contains0 = false; + if(temp == 0) contains0 = true; + + while(temp != -1 && dist[temp] >= 0) { + answer.push(temp); + temp = prev_flight[temp]; + if(temp == 0) contains0 = true; + } + + if(contains0) { + pw.println(dist[N-1]); + while(!answer.isEmpty()) { + pw.print(answer.peekFirst()+1); + answer.pop(); + if(!answer.isEmpty()) pw.print(" "); + } + pw.println(); + } else { + pw.println("IMPOSSIBLE\n"); + } + pw.close(); + } + + //practically a necessity for Java users on CSES + static class FastIO { + + InputStream dis; + byte[] buffer = new byte[1 << 17]; + int pointer = 0; + + public FastIO(String fileName) throws Exception { + dis = new FileInputStream(fileName); + } + + public FastIO(InputStream is) throws Exception { + dis = is; + } + + int nextInt() throws Exception { + int ret = 0; + + byte b; + do { + b = nextByte(); + } while (b <= ' '); + boolean negative = false; + if (b == '-') { + negative = true; + b = nextByte(); + } + while (b >= '0' && b <= '9') { + ret = 10 * ret + b - '0'; + b = nextByte(); + } + + return (negative) ? -ret : ret; + } + + long nextLong() throws Exception { + long ret = 0; + + byte b; + do { + b = nextByte(); + } while (b <= ' '); + boolean negative = false; + if (b == '-') { + negative = true; + b = nextByte(); + } + while (b >= '0' && b <= '9') { + ret = 10 * ret + b - '0'; + b = nextByte(); + } + + return (negative) ? -ret : ret; + } + + byte nextByte() throws Exception { + if (pointer == buffer.length) { + dis.read(buffer, 0, buffer.length); + pointer = 0; + } + return buffer[pointer++]; + } + + String next() throws Exception { + StringBuffer ret = new StringBuffer(); + + byte b; + do { + b = nextByte(); + } while (b <= ' '); + while (b > ' ') { + ret.appendCodePoint(b); + b = nextByte(); + } + + return ret.toString(); + } + + } +} + + ``` + +</JavaSection> + +</LanguageSection> + +</Spoiler> + +<!-- However, not all problems clearly give you directed acyclic graphs (ex. [Plat - Cave Paintings](http://usaco.org/index.php?page=viewproblem2&cpid=996)). An important step in many problems is to reduce the statement into a directed acyclic graph. See the editorial of the linked problem for more information. + +(Ben - this last paragraph doesn't seem very helpful.) --> + +## Problems + +<Problems problems={problems.general} /> diff --git a/content/4_Gold/Tree_Euler.mdx b/content/4_Gold/Tree_Euler.mdx new file mode 100644 index 0000000..5c43b7c --- /dev/null +++ b/content/4_Gold/Tree_Euler.mdx @@ -0,0 +1,66 @@ +--- +id: tree-euler +title: "Euler Tour Technique" +author: "Benjamin Qi" +prerequisites: + - dfs + - SRQ + - PURS +description: "Flattening a tree into an array to easily query and update subtrees." +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Subtree Queries", "1137", "Easy", false, ["Euler-Tree"], "equivalent to https://judge.yosupo.jp/problem/vertex_add_subtree_sum"), + new Problem("CSES", "Path Queries", "1138", "Easy", false, ["Euler-Tree","PURS"], "equivalent to https://judge.yosupo.jp/problem/vertex_add_path_sum"), + ], + lca: [ + new Problem("CSES", "Company Queries II", "1688", "Easy", false, ["LCA"], ""), + new Problem("CSES", "Distance Queries", "1135", "Easy", false, ["LCA"], ""), + ], + problems: [ + 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("Plat", "Snow-Cow", "973", "Hard", false, ["Euler-Tree","PURS"], ""), + ] +}; + +## Introduction + +<Problems problems={problems.sample} /> + +If we can preprocess a rooted tree such that every subtree corresponds to a contiguous range on an array, we can do updates and range queries on it! + +### Tutorial + +<Resources> + <Resource source="CPH" title="18.2 - Subtrees & Paths" starred>introduces tree traversal array</Resource> +</Resources> + +### Implementation + +<IncompleteSection /> + +## LCA + +<Problems problems={problems.lca} /> + +### Tutorial + +<Resources> + <Resource source="CPH" title="18.3 - Least Common Ancestor (Method 2)" starred></Resource> + <Resource source="cp-algo" title="Reducing LCA to RMQ" url="graph/lca.html" starred></Resource> +</Resources> + +### Implementation + +<IncompleteSection /> + +## Problems + +<Problems problems={problems.problems} />
\ No newline at end of file diff --git a/content/4_Gold/factoralgorithm1.png b/content/4_Gold/factoralgorithm1.png Binary files differnew file mode 100644 index 0000000..9b12c92 --- /dev/null +++ b/content/4_Gold/factoralgorithm1.png diff --git a/content/4_Gold/factoralgorithm2.png b/content/4_Gold/factoralgorithm2.png Binary files differnew file mode 100644 index 0000000..d0629bf --- /dev/null +++ b/content/4_Gold/factoralgorithm2.png diff --git a/content/4_Silver/Amortized.mdx b/content/4_Silver/Amortized.mdx deleted file mode 100644 index 109f07c..0000000 --- a/content/4_Silver/Amortized.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -id: amortized -title: "Amortized Analysis" -author: Darren Yao -prerequisites: - - Silver - Introduction to Sorting - - Silver - Stacks & Queues -description: "?" -frequency: 2 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Playlist", "1141", "Intro", false, []), - new Problem("CSES", "Sum of Two Values", "1141", "Easy", false, []), - ], - general: [ - new Problem("Silver", "Diamond Collector", "643", "Normal", false, ["2P", "Sorting"], ""), - new Problem("Silver", "Paired Up", "738", "Normal", false, ["2P", "Sorting"]), - new Problem("Gold", "Haybale Feast", "767", "Normal", false, ["Set", "Sliding Window"]), - new Problem("Plat", "Fort Moo", "600", "Normal", false, ["Sliding Window"]), - new Problem("CF", "Books", "problemset/problem/279/B", "Normal", false, []), - new Problem("CF", "Cellular Network", "problemset/problem/702/C", "Normal", false, []), - new Problem("CF", "USB vs. PS/2", "problemset/problem/762/B", "Normal", false, []), - new Problem("CF", "K-Good Segment", "problemset/problem/616/D", "Normal", false, []), - new Problem("CF", "Garland", "problemset/problem/814/C", "Normal", false, []), - new Problem("CF", "Jury Meeting", "problemset/problem/853/B", "Normal", false, []), - ], - qs: [ - new Problem("YS","Queue Composite","queue_operate_all_composite","Hard",false,[],""), - ], - } -}; - -## Sample - -<problems-list problems={metadata.problems.sample} /> - -## Two Pointers - -Two pointers refers to iterating two monotonic pointers across an array to search for a pair of indices satisfying some condition in linear time. - - - [CSES Playlist](https://cses.fi/problemset/task/1141) - -### Tutorials - - - Intro to USACO 14.1 - - CPH 8.1 (Amortized Analysis) - -## Sliding Window - -Let's envision a sliding window (or constant size subarray) of size $K$ moving left to right along an array, $a$. For each position of the window, we want to compute some information. - -Let's store a `std::set` of integers representing the integers inside the window. If the window currently spans the range $i \dots j$, we observe that moving the range forward to $i+1 \dots j+1$ only removes $a_i$ and adds $a_{j+1}$ to the window. We can support these two operations and query for the minimum/maximum in the set in $O(\log N)$. - -To compute the sum in the range, instead of using a set, we can store a variable $s$ representing the sum. As we move the window forward, we update $s$ by performing the operations $s -= a_i$ and $s += a_{j+1}$. - -### Min Queue - -- [cp-algorithms: Min Stack + Queue](https://cp-algorithms.com/data_structures/stack_queue_modification.html) - - learn about the "min queue" that CPH describes. - -### Queue w/ Two Stacks - -<problems-list problems={metadata.problems.qs} /> - -<optional-content title="Application"> - -Used to remove a factor of $O(\log N)$ in [USACO Plat - Mowing Mischief](http://www.usaco.org/index.php?page=viewproblem2&cpid=926). - -</optional-content> - -### Further Reading - -- [Medium](https://levelup.gitconnected.com/an-introduction-to-sliding-window-algorithms-5533c4fe1cc7) -- [G4G](https://www.geeksforgeeks.org/window-sliding-technique/) - -## Problems - -<problems-list problems={metadata.problems.general} /> - -<optional-content title="Better Memory with Two Passes"> - -- [Plat - Train Tracking](http://www.usaco.org/index.php?page=viewproblem2&cpid=841) - - Extremely difficult. - -</optional-content>
\ No newline at end of file diff --git a/content/4_Silver/BFS.mdx b/content/4_Silver/BFS.mdx deleted file mode 100644 index fd8e575..0000000 --- a/content/4_Silver/BFS.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -id: bfs -title: "Breadth First Search" -author: Benjamin Qi -prerequisites: - - Silver - Depth First Search -description: "Traversing a graph in a way such that vertices closer to the starting vertex are processed first." -frequency: 2 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Message Route", "1667", "Easy", false, ["BFS"]), - ], - general: [ - new Problem("CSES", "Monsters", "1194", "Easy", false, ["BFS"]), - new Problem("Gold", "Cow Navigation", "695", "Easy", false, ["BFS"], "lots of casework"), - 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"]), - ], - } -}; - -<info-block title="Pro Tip"> -No silver problem should require BFS rather than DFS, but it's still good to know at this level. -</info-block> - -## Sample - -<problems-list problems={metadata.problems.sample} /> - -## Tutorial - - - CPH 12.2 - - [PAPS 12.1](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [CSAcademy BFS](https://csacademy.com/lesson/breadth_first_search) - - [cp-algo BFS](https://cp-algorithms.com/graph/breadth-first-search.html) - - [cp-algo 0/1 BFS](https://cp-algorithms.com/graph/01_bfs.html) - - [KhanAcademy BFS](https://www.khanacademy.org/computing/computer-science/algorithms/breadth-first-search/a/breadth-first-search-and-its-uses) - -## Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/4_Silver/Binary_Search.mdx b/content/4_Silver/Binary_Search.mdx deleted file mode 100644 index 136c10c..0000000 --- a/content/4_Silver/Binary_Search.mdx +++ /dev/null @@ -1,160 +0,0 @@ ---- -id: binary-search -title: "Binary Search on the Answer" -author: Darren Yao -prerequisites: - - Silver - Introduction to Sorting -description: You should already be familiar with the concept of binary searching for a number in a sorted array. However, binary search can be extended to binary searching on the answer itself. -frequency: 3 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("CSES", "Factory Machines", "1620", "Easy", false, []), - new Problem("Silver", "Moo Buzz", "966", "Intro", false, [], "binary search not required"), - new Problem("Silver", "Cow Dance Show", "690", "Normal", false, [], "binary search on $K$ and simulate"), - new Problem("Silver", "Convention", "858", "Normal", 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", "Normal", 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("Silver", "Loan Repayment", "991", "Hard", false, [], "requires some rather tricky analysis to speed up naive $O(N\log N)$ solution"), - new Problem("CF", "The Meeting Place Cannot Be Changed", "contest/782/problem/B", "Normal", false, [], ""), - new Problem("CF", "Preparing for Merge Sort", "contest/847/problem/B", "Normal", false, [], ""), - new Problem("CF", "Level Generation", "problemset/problem/818/F", "Normal", false, [], ""), - new Problem("CF", "Packmen", "contest/847/problem/E", "Normal", false, [], ""), - new Problem("CF", "Office Keys", "problemset/problem/830/A", "Normal", false, [], ""), - ], - } -}; - -<info-block title="Pro Tip"> - -Quite frequent at the Silver level. - -</info-block> - -When we binary search on the answer, we start with a search space of size $N$ which we know the answer lies in. Then, each iteration of the binary search cuts the search space in half, so the algorithm tests $O(\log N)$ values. This is efficient and much better than testing each possible value in the search space. - -Let's say we have a function `check(x)` that returns true if the answer of $x$ is possible, and false otherwise. Usually, in such problems, we'll want to find the maximum or minimum value of $x$ such that `check(x)` is true. Similarly to how binary search on an array only works on a sorted array, binary search on the answer only works if the answer function is [monotonic](https://en.wikipedia.org/wiki/Monotonic_function), meaning that it is always non-decreasing or always non-increasing. - -In particular, if we want to find the maximum `x` such that `check(x)` is true, then we can binary search if `check(x)` satisfies both of the following conditions: - - - If `check(x)` is `true`, then `check(y)` is true for all $y \leq x$. - - If `check(x)` is `false`, then `check(y)` is false for all $y \geq x$. - -In other words, we want to reduce the search space to something of the following form, using a check function as we described above. - -<center>true true true true true false false false false</center> - -We want to find the point at which `true` becomes `false`. - -Below, we present two algorithms for binary search. The first implementation may be more intuitive, because it's closer to the binary search most students learned, while the second implementation is shorter. - -(pseudocode) - -If instead we're looking for the minimum `x` that satisfies some condition, then we can binary search if `check(x)` satisfies both of the following conditions: - - - If `check(x)` is true, then `check(y)` is true for all $y \geq x$. - - If `check(x)` is false, then `check(y)` is false for all $y \leq x$. - -The binary search function for this is very similar. Find the maximum value of $x$ such that `check(x)` is false with the algorithm above, and return $x+1$. - -## Example: [CF 577 Div. 2 C](https://codeforces.com/contest/1201/problem/C) - -Given an array `arr` of $n$ integers, where $n$ is odd, we can perform the following operation on it $k$ times: take any element of the array and increase it by $1$. We want to make the median of the array as large as possible, after $k$ operations. - -Constraints: $1 \leq n \leq 2 \cdot 10^5, 1 \leq k \leq 10^9$ and $n$ is odd. - -The solution is as follows: we first sort the array in ascending order. Then, we binary search for the maximum possible median. We know that the number of operations required to raise the median to $x$ increases monotonically as $x$ increases, so we can use binary search. For a given median value $x$, the number of operations required to raise the median to $x$ is - -$$\sum_{i=(n+1)/2}^{n} \max(0, x - \text{arr[i]})$$ - -If this value is less than or equal to $k$, then $x$ can be the median, so our check function returns true. Otherwise, $x$ cannot be the median, so our check function returns false. - -The solution codes use the second implementation of binary search. - -Java: - -```java -static int n; -static long k; -static long[] arr; -public static void main(String[] args) { - - n = r.nextInt(); k = r.nextLong(); - arr = new long[n]; - for(int i = 0; i < n; i++){ - arr[i] = r.nextLong(); - } - Arrays.sort(arr); - - pw.println(search()); - pw.close(); -} - -// binary searches for the correct answer -static long search(){ - long pos = 0; long max = (long)2E9; - for(long a = max; a >= 1; a /= 2){ - while(check(pos+a)) pos += a; - } - return pos; -} - -// checks whether the number of given operations is sufficient -// to raise the median of the array to x -static boolean check(long x){ - long operationsNeeded = 0; - for(int i = (n-1)/2; i < n; i++){ - operationsNeeded += Math.max(0, x-arr[i]); - } - if(operationsNeeded <= k){ return true; } - else{ return false; } -} -``` - -C++: - -```cpp -int n; -long long k; -vector<long long> v; - -// checks whether the number of given operations is sufficient -// to raise the median of the array to x -bool check(long long x){ - long long operationsNeeded = 0; - for(int i = (n-1)/2; i < n; i++){ - operationsNeeded += max(0, x-v[i]); - } - if(operationsNeeded <= k) return true; - else return false; -} - -// binary searches for the correct answer -long long search(){ - long long pos = 0; long long max = 2E9; - for(long long a = max; a >= 1; a /= 2){ - while(check(pos+a)) pos += a; - } - return pos; -} - -int main() { - cin >> n >> k; - for(int i = 0; i < n; i++){ - int t; - cin >> t; - v.push_back(t); - } - sort(v.begin(), v.end()); - - cout << search() << '\n'; -} -``` - -### Problems - -<problems-list problems={metadata.problems.general} /> diff --git a/content/4_Silver/DFS.mdx b/content/4_Silver/DFS.mdx deleted file mode 100644 index 90d857b..0000000 --- a/content/4_Silver/DFS.mdx +++ /dev/null @@ -1,120 +0,0 @@ ---- -id: dfs -title: Depth First Search -author: Siyong Huang -prerequisites: - - Bronze - Introduction to Graphs -description: A way to traverse a graph using recursion. -frequency: 4 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Building Roads", "1666", "Intro", false, ["DFS"]), - ], - tree: [ - new Problem("CSES", "Subordinates", "1674", "Intro", false, ["Tree", "DFS"]), - new Problem("Silver", "Mootube", "788", "Easy", false, ["Tree", "DFS"]), - new Problem("CF", "Journey", "contest/839/problem/C", "Easy", false, ["Tree", "DFS"]), - new Problem("CF", "PolandBall & Forest", "problemset/problem/755/C", "Easy", false, ["Tree", "DFS"]), - new Problem("CSES", "Tree Diameter", "1131", "Normal", false, ["Tree", "DFS"]), - new Problem("CSES", "Tree Distances I", "1132", "Normal", false, ["Tree", "DFS"]), - new Problem("CSES", "Tree Distances II", "1133", "Normal", false, ["Tree", "DFS"]), - new Problem("CF", "Wizard's Tour", "contest/860/problem/D", "Normal", false, ["Tree", "DFS"]), - new Problem("POI", "Hotels", "https://szkopul.edu.pl/problemset/problem/gDw3iFkeVm7ZA3j_16-XR7jI/site/?key=statement", "Normal", false, ["Tree", "DFS"]), - new Problem("CSA", "Tree Construction", "contest/860/problem/D", "Hard", false, ["Tree", "DFS"], "several cases"), - ], - general: [ - new Problem("CF", "Bear & Friendship", "problemset/problem/771/A", "Easy", false, ["DFS"]), - new Problem("Silver", "Closing the Farm", "644", "Easy", false, ["DFS"]), - new Problem("Silver", "Moocast", "668", "Easy", false, ["DFS"]), - new Problem("Silver", "Fence Planning", "944", "Easy", false, ["DFS"]), - new Problem("Kattis", "Birthday Party", "birthday", "Easy", false, ["DFS"], "DFS with each edge removed"), - new Problem("Silver", "Milk Visits", "968", "Normal", false, ["DFS"]), - 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"]), - ], - bipsample: [ - new Problem("CSES", "Building Teams", "1668", "Easy", false, ["Bipartite"]), - ], - bip: [ - new Problem("CF", "Bipartiteness", "contest/862/problem/B", "Easy", false, ["Bipartite"]), - new Problem("Silver", "The Great Revegetation", "920", "Easy", false, ["Bipartite"]), - ], - } -}; - - -<problems-list problems={metadata.problems.sample} /> - -<info-block title="Pro Tip"> - -Quite frequent at the Silver level. - -</info-block> - -## Tutorial - - - Recommended: - - CPH 12.1 (DFS), 14 (Tree algorithms) - - [PAPS 12.2](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [CSAcademy DFS](https://csacademy.com/lesson/depth_first_search/) - - Additional: - - [CPC.7](https://github.com/SuprDewd/T-414-AFLV/tree/master/07_graphs_1) - - [cp-algo DFS](https://cp-algorithms.com/graph/depth-first-search.html) - - hard to parse if this is your first time learning about DFS - - [Topcoder Graphs Pt 2](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-graphs-and-their-data-structures-section-2/) - -### Problems - - - Trees - -<problems-list problems={metadata.problems.tree} /> - - - General - -<problems-list problems={metadata.problems.general} /> - -## Graph Two-Coloring - -*Graph two-coloring* refers to assigning a boolean value to each node of the graph, dictated by the edge configuration -The most common example of a two-colored graph is a *bipartite graph*, in which each edge connects two nodes of opposite colors. - -<problems-list problems={metadata.problems.bipsample} /> - -### Tutorial - - - CPH 12.3 - - [cp-algo - bipartite check](https://cp-algorithms.com/graph/bipartite-check.html) - - Uses BFS, but DFS accomplishes the same task - -The idea is that we can arbitrarily label a node and then run DFS. Every time we visit a new (unvisited) node, we set its color based on the edge rule. When we visit a previously visited node, check to see whether its color matches the edge rule. For example, an implementation of coloring a bipartite graph is shown below. - -```cpp -//UNTESTED - -bool is_bipartite = true; -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); - } -} -``` - -### Problems - -<problems-list problems={metadata.problems.bip} /> diff --git a/content/4_Silver/Flood_Fill.mdx b/content/4_Silver/Flood_Fill.mdx deleted file mode 100644 index 34bf2e5..0000000 --- a/content/4_Silver/Flood_Fill.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: ff -title: Flood Fill -author: Siyong Huang -prerequisites: - - Silver - Depth First Search -description: Finding connected components in a graph that is respresented by a grid. -frequency: 3 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Counting Rooms", "1192", "Easy", false, []), - ], - general: [ - new Problem("CSES", "Labyrinth", "1193", "Easy", false, []), - new Problem("Silver", "Count Cross", "716", "Easy", false, ["FF"]), - new Problem("Silver", "Ice Perimeter", "895", "Easy", false, []), - new Problem("Silver", "Switching on the Lights", "570", "Normal", false, []), - new Problem("Silver", "Build Gates", "596", "Normal", false, []), - new Problem("Silver", "Milk Pails", "620", "Normal", false, []), - new Problem("Silver", "Where's Bessie?", "740", "Normal", false, []), - new Problem("Silver", "Why Did the Cow Cross the Road III", "716", "Normal", false, []), - new Problem("Silver", "Multiplayer Moo", "836", "Hard", false, []), - new Problem("Silver", "Snow Boots", "811", "Hard", false, []), - new Problem("Silver", "Mooyo Mooyo", "860", "Hard", false, []), - ], - } -}; - -<info-block title="Pro Tip"> - -Quite frequent at the Silver level. - -</info-block> - -<problems-list problems={metadata.problems.sample} /> - -## [Flood Fill](https://en.wikipedia.org/wiki/Flood_fill) - -### Tutorial - - - Recommended: - - Intro to USACO Ch 10 - -## Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/4_Silver/Func_Graphs.mdx b/content/4_Silver/Func_Graphs.mdx deleted file mode 100644 index e95eb0a..0000000 --- a/content/4_Silver/Func_Graphs.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -id: func-graphs -title: Functional Graphs -author: Siyong Huang -prerequisites: - - Silver - Depth First Search -description: A functional graph is a digraph in which every vertex has exactly one outgoing edge. -frequency: 1 ---- - - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CF", "Div 2 B - Badge", "contest/1020/problem/B", "Intro", false, ["Func Graph"], "Try to solve the problem in $O(N)$!"), - ], - general: [ - new Problem("Silver", "The Bovine Shuffle", "764", "Normal", false, ["Func Graph"], "Try to solve the problem in $O(N)$!"), - new Problem("CSES", "Planets Cycles", "1751", "Normal", false, ["Func Graph"], ""), - new Problem("Silver", "Swapity Swapity Swap", "1014", "Normal", false, ["Permutation"], ""), - new Problem("POI", "Mafia", "https://szkopul.edu.pl/problemset/problem/w3YAoAT3ej27YeiaNWjK57_G/site/?key=statement", "Hard", false, ["Func Graph"], ""), - new Problem("POI", "Spies", "https://szkopul.edu.pl/problemset/problem/r6tMTfvQFPAEfQioYMCQndQe/site/?key=statement", "Hard", false, [], ""), - new Problem("POI", "Frog", "https://szkopul.edu.pl/problemset/problem/qDH9CkBHZKHY4vbKRBlXPrA7/site/?key=statement", "Hard", false, [], ""), - ], - } -}; - -Consider graphs like the one presented in this problem: - -<problems-list problems={metadata.problems.sample} /> - -### Tutorial - - - CPH 16.3: successor paths - - CPH 16.4: cycle detection in successor graph - -Aka **successor graph**. - -The following sample code counts the number of cycles in such a graph. The "stack" contains nodes that can reach the current node. If the current node points to a node `v` on the stack (`on_stack[v]` is true), then we know that a cycle has been created. However, if the current node points to a node `v` that has been previously visited but is not on the stack, then we know that the current chain of nodes points into a cycle that has already been considered. - -```cpp -//UNTESTED - -//Each node points to next_node[node] - -bool visited[MAXN], on_stack[MAXN]; -int number_of_cycles = 0, next_node[MAXN]; -void dfs(int n) -{ - visited[n] = on_stack[n] = true; - int u = next_node[n]; - if(on_stack[u]) - number_of_cycles++; - else if(!visited[u]) - dfs(u); - on_stack[n] = false; -} -int main() -{ - //read input, etc - for(int i = 1;i <= N;i++) - if(!visited[i]) - dfs(i); -} -``` - -(floyd's algo?) - -### Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/4_Silver/Greedy.mdx b/content/4_Silver/Greedy.mdx deleted file mode 100644 index 90422a1..0000000 --- a/content/4_Silver/Greedy.mdx +++ /dev/null @@ -1,204 +0,0 @@ ---- -id: greedy -title: "Greedy Algorithms" -author: Darren Yao -prerequisites: - - Silver - Sorting with Custom Comparators -description: Greedy algorithms select the optimal choice at each step instead of looking at the solution space as a whole. This reduces the problem to a smaller problem at each step. -frequency: 3 ---- - -import {Problem} from "../models" - -export const metadata = { - problems: { - practice: [ - new Problem("Silver", "Lemonade Line", "835") - ] - } -}; - -## Additional Reading - - - CPH 6 - - [PAPS 8](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [CPC.5](https://github.com/SuprDewd/T-414-AFLV/tree/master/05_greedy_algorithms) - -# Greedy - -Greedy does not refer to a single algorithm, but rather a way of thinking that is applied to problems. There's no one way to do greedy algorithms. Hence, we use a selection of well-known examples to help you understand the greedy paradigm. - -Usually, when using a greedy algorithm, there is a heuristic or value function that determines which choice is considered most optimal. Usually (but not always) some sorting step is involved. - -## Example: Studying Algorithms - -### Statement - -Steph wants to improve her knowledge of algorithms over winter break. She has a total of $X$ ($1 \leq X \leq 10^4$) minutes to dedicate to learning algorithms. There are $N$ ($1 \leq N \leq 100$) algorithms, and each one of them requires $a_i$ ($1 \leq a_i \leq 100$) minutes to learn. Find the maximum number of algorithms she can learn. - -### Solution - -The first observation we make is that Steph should prioritize learning algorithms from easiest to hardest; in other words, start with learning the algorithm that requires the least amount of time, and then choose further algorithms in increasing order of time required. Let's look at the following example: - -$$X = 15, \qquad N = 6, \qquad a_i = \{ 4, 3, 8, 4, 7, 3 \}$$ - -After sorting the array, we have $\{ 3, 3, 4, 4, 7, 8 \}$. Within the maximum of 15 minutes, Steph can learn four algorithms in a total of $3+3+4+4 = 14$ minutes. -The implementation of this algorithm is very simple. We sort the array, and then take as many elements as possible while the sum of times of algorithms chosen so far is less than $X$. Sorting the array takes $O(N \log N)$ time, and iterating through the array takes $O(N)$ time, for a total time complexity of $O(N \log N)$. - -```java -// read in the input, store the algorithms in int[] algorithms -Arrays.sort(algorithms); -int count = 0; // number of minutes used so far -int i = 0; -while(count + algorithms[i] <= x){ - // while there is enough time, learn more algorithms - count += algorithms[i]; - i++; -} -pw.println(i); // print the ans -pw.close(); -``` - -## The Scheduling Problem - -There are $N$ events, each described by their starting and ending times. Jason would like to attend as many events as possible, but he can only attend one event at a time, and if he chooses to attend an event, he must attend the entire event. Traveling between events is instantaneous. - -### Bad Greedy: Earliest Starting Next Event -One possible ordering for a greedy algorithm would always select the next possible event that begins as soon as possible. Let's look at the following example, where the selected events are highlighted in red: - -<!-- \begin{center} - \begin{tikzpicture}[ultra thick] - \draw[red](1, 2.5) -- (4, 2.5); - \draw(2, 2) -- (5, 2); - \draw[red](5, 1.5) -- (7, 1.5); - \draw(6, 1) -- (7, 1); - \end{tikzpicture} -\end{center} --> - -In this example, the greedy algorithm selects two events, which is optimal. However, this doesn't always work, as shown by the following counterexample: - -<!-- \begin{center} - \begin{tikzpicture}[ultra thick] - \draw[red](1, 2.5) -- (10, 2.5); - \draw(2, 2) -- (5, 2); - \draw(6, 1.5) -- (7, 1.5); - \draw(8, 1) -- (11, 1); - \end{tikzpicture} -\end{center} --> - -In this case, the greedy algorithm selects to attend only one event. However, the optimal solution would be the following: - -<!-- \begin{center} - \begin{tikzpicture}[ultra thick] - \draw(1, 2.5) -- (10, 2.5); - \draw[red](2, 2) -- (5, 2); - \draw[red](6, 1.5) -- (7, 1.5); - \draw[red](8, 1) -- (11, 1); - \end{tikzpicture} -\end{center} --> - -### Correct Greedy: Earliest Ending Next Event - -Instead, we can select the event that ends as early as possible. This correctly selects the three events. - -<!-- \begin{center} - \begin{tikzpicture}[ultra thick] - \draw(1, 2.5) -- (10, 2.5); - \draw[red](2, 2) -- (5, 2); - \draw[red](6, 1.5) -- (7, 1.5); - \draw[red](8, 1) -- (11, 1); - \end{tikzpicture} -\end{center} --> - -In fact, this algorithm always works. A brief explanation of correctness is as follows. If we have two events $E_1$ and $E_2$, with $E_2$ ending later than $E_1$, then it is always optimal to select $E_1$. This is because selecting $E_1$ gives us more choices for future events. If we can select an event to go after $E_2$, then that event can also go after $E_1$, because $E_1$ ends first. Thus, the set of events that can go after $E_2$ is a subset of the events that can go after $E_1$, making $E_1$ the optimal choice. - -For the following code, let's say we have the array `events` of events, which each contain a start and an end point. We'll be using the following static class to store each event (a review of the previous chapter!) - -```java -static class Event implements Comparable<Event>{ - int start; int end; - public Event(int s, int e){ - start = s; end = e; - } - public int compareTo(Event e){ - return Integer.compare(this.end, e.end); - } -} -``` - -```java -// read in the input, store the events in Event[] events. -Arrays.sort(events); // sorts by comparator we defined above -int currentEventEnd = -1; // end of event currently attending -int ans = 0; // how many events were attended? -for(int i = 0; i < n; i++){ // process events in order of end time - if(events[i].start >= currentEventEnd){ // if event can be attended - // we know that this is the earliest ending event that we can attend - // because of how the events are sorted - currentEventEnd = events[i].end; - ans++; - } -} -pw.println(ans); -pw.close(); -``` - -## When Greedy Fails - -We'll provide a few common examples of when greedy fails, so that you can avoid falling into obvious traps and wasting time getting wrong answers in contest. - -### Coin Change - -This problem gives several coin denominations, and asks for the minimum number of coins needed to make a certain value. Greedy algorithms can be used to solve this problem only in very specific cases (it can be proven that it works for the American as well as the Euro coin systems). However, it doesn't work in the general case. For example, let the coin denominations be $\{1, 3, 4\}$, and say the value we want is 6. The optimal solution is $\{3, 3\}$, which requires only two coins, but the greedy method of taking the highest possible valued coin that fits in the remaining denomination gives the solution $\{4, 1, 1\}$, which is incorrect. - -### Knapsack - -The knapsack problem gives a number of items, each having a weight and a value, and we want to choose a subset of these items. We are limited to a certain weight, and we want to maximize the value of the items that we take. - -Let's take the following example, where we have a maximum capacity of 4: -<!-- \begin{center} - \begin{tabular}{c c c c} - \toprule - Item & Weight & Value & Value Per Weight \\ - \midrule - A & 3 & 18 & 6 \\ - B & 2 & 10 & 5 \\ - C & 2 & 10 & 5 \\ - \bottomrule - \end{tabular} -\end{center} --> - -If we use greedy based on highest value first, we choose item A and then we are done, as we don't have remaining weight to fit either of the other two. Using greedy based on value per weight again selects item A and then quits. However, the optimal solution is to select items B and C, as they combined have a higher value than item A alone. In fact, there is no working greedy solution. The solution to this problem uses **dynamic programming**, which is covered in gold. - -# Problems - -## Standard Problems - -- https://cses.fi/problemset/task/1643 - - Max Subarray Sum -- https://cses.fi/problemset/task/1629 - - Job Scheduling - -## Practice Problems? - -- https://cses.fi/problemset/task/1073 - - - USACO Silver - - [Lemonade Line](http://usaco.org/index.php?page=viewproblem2&cpid=835) - - [Why Did the Cow Cross the Road](http://www.usaco.org/index.php?page=viewproblem2&cpid=714) - - first step: sort! - - [Berry Picking](http://www.usaco.org/index.php?page=viewproblem2&cpid=990) - - [Rest Stops](http://www.usaco.org/index.php?page=viewproblem2&cpid=810) - - [High Card Wins](http://usaco.org/index.php?page=viewproblem2&cpid=571) - - Misc - - [Sure Bet](https://csacademy.com/contest/archive/task/sure-bet/) - - [Did you Mean...](http://codeforces.com/contest/860/problem/A) - - [Permutation](http://codeforces.com/problemset/problem/864/D) - - [Bus](http://codeforces.com/problemset/problem/864/C) - - [Kayaking](http://codeforces.com/problemset/problem/863/B) - - [CSES Stick Division](https://cses.fi/problemset/task/1161/) - - Huffman Coding? - - [New Year and Three Musketeers](https://codeforces.com/contest/611/problem/E) - - Harder problem, but still greedy! Requires knowledge of maps. - -<problems-list problems={metadata.problems.practice} />
\ No newline at end of file diff --git a/content/4_Silver/Maps_Sets.mdx b/content/4_Silver/Maps_Sets.mdx deleted file mode 100644 index 887aafe..0000000 --- a/content/4_Silver/Maps_Sets.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -id: maps-sets -title: "More with Maps & Sets" -author: Benjamin Qi -prerequisites: - - Bronze - Data Structures -description: "More with iterators?" -frequency: 2 ---- - -(set iterators?) - -## USACO Problems - - - [CSES Bit Inversions](https://cses.fi/problemset/task/1188/) - - Silver - - [Cities & States](http://usaco.org/index.php?page=viewproblem2&cpid=667) - - [Milk Measurement](http://usaco.org/index.php?page=viewproblem2&cpid=763) - - [Convention II](http://usaco.org/index.php?page=viewproblem2&cpid=859) - - Gold - - [Snow Boots](http://www.usaco.org/index.php?page=viewproblem2&cpid=813) - - [Springboards](http://www.usaco.org/index.php?page=viewproblem2&cpid=995) - - hard? - -## CF Problems - - - [Jury Marks](http://codeforces.com/contest/831/problem/C) [](67) - - Hard, would recommend skipping (1700 on CF) - - [Mahmoud & Ehab & Function](http://codeforces.com/contest/862/problem/E) [](74) - - Hard, do not attempt until Gold/Plat (2100 on CF) - - [Tournament](http://codeforces.com/contest/878/problem/C) [](106) - - First solve problem for $n$-th tournament only - - Extremely hard, do not attempt (2700 on CF)
\ No newline at end of file diff --git a/content/4_Silver/Prefix_Sums.mdx b/content/4_Silver/Prefix_Sums.mdx deleted file mode 100644 index df833db..0000000 --- a/content/4_Silver/Prefix_Sums.mdx +++ /dev/null @@ -1,238 +0,0 @@ ---- -id: prefix-sums -title: "Prefix Sums" -author: Darren Yao, Eric Wei -description: Computing range sum queries in constant time over a fixed array. -frequency: 3 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("YS", "Static Range Sum", "static_range_sum", "Easy", false, [], "equivalent to [CSES Range Sum Queries I](https://cses.fi/problemset/task/1646)"), - ], - cum: [ - new Problem("LC", "Find Pivot Index", "find-pivot-index", "Easy", false, [], ""), - new Problem("Silver", "Breed Counting", "572", "Easy", false, [], ""), - new Problem("Silver", "Subsequences Summing to Seven", "595", "Easy", false, [], ""), - new Problem("Silver", "Hoof Paper Scissors", "691", "Easy", false, [], ""), - new Problem("Silver", "Max Cross", "715", "Easy", false, [], ""), - 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("CF", "Edu C: Magic Ship", "problemset/problem/1117/", "Normal", false, [], ""), - ], - maxsum: [ - new Problem("CSES", "Max Subarray Sum", "1643", "Easy", false, ["Prefix Sums"], ""), - ], - maxsumext: [ - new Problem("CSES", "Max Subarray Sum II", "1644", "Easy", false, ["Prefix Sums"], ""), - ], - sample2: [ - new Problem("CSES", "Forest Queries", "1652", "Easy", false, ["Prefix Sums"], ""), - ], - cum2: [ - new Problem("Silver", "Painting the Barn", "919", "Easy", false, ["Prefix Sums"], ""), - new Problem("Gold", "Painting the Barn", "923", "Hard", false, ["Prefix Sums", "Max Subarray Sum"], ""), - ], - related: [ - new Problem("Silver", "My Cow Ate My Homework", "762", "Easy", false, ["Prefix Sums"], ""), - new Problem("CSES", "Range XOR Queries", "1650", "Easy", false, ["Prefix Sums"], ""), - ], - complex: [ - new Problem("Google Kick Start", "Candies (Test Set 1)", "https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ff43/0000000000337b4d", "Easy", false, ["Prefix Sums"], ""), - new Problem("AC", "Multiple of 2019", "https://atcoder.jp/contests/abc164/tasks/abc164_d", "Hard", false, ["Prefix Sums"], ""), - ], - } -}; - -<info-block title="Pro Tip"> - -Quite frequent at the Silver level. - -</info-block> - -<problems-list problems={metadata.problems.sample} /> - -## Additional Resources - - - CPH 9.1 - - [PAPS 11.2.1](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - -## Tutorial - -Let's say we have a one-indexed integer array $\texttt{arr}$ of size $N$ and we want to compute the value of -$$ -\texttt{arr}[a]+\texttt{arr}[a+1]+\cdots+\texttt{arr}[b] -$$ -for $Q$ different pairs $(a,b)$ satisfying $1\le a\le b\le N$. We'll use the following example with $N = 6$: - -<center> - -| Index $i$ | 1 | 2 | 3 | 4 | 5 | 6 | -| --- | --- | --- | --- | --- | --- | --- | -| $\texttt{arr}[i]$ | 1 | 6 | 4 | 2 | 5 | 3 | - -</center> - -Naively, for every query, we can iterate through all entries from index $a$ to index $b$ to add them up. Since we have $Q$ queries and each query requires a maximum of $O(N)$ operations to calculate the sum, our total time complexity is $O(NQ)$. For most problems of this nature, the constraints will be $N, Q \leq 10^5$, so $NQ$ is on the order of $10^{10}$. This is not acceptable; it will almost certainly exceed the time limit. - -Instead, we can use prefix sums to process these array sum queries. We designate a prefix sum array $\texttt{prefix}$. First, because we're 1-indexing the array, set $\texttt{prefix}[0]=0$, then for indices $k$ such that $0 \leq k \leq n$, define the prefix sum array as follows: - -$$ -\texttt{prefix}[k]=\sum_{i=1}^{k} \texttt{arr}[i] -$$ - -Basically, what this means is that the element at index $k$ of the prefix sum array stores the sum of all the elements in the original array from index $1$ up to $k$. This can be calculated easily in $O(N)$ by the following formula for each $1\le k\le n$: - -$$ -\texttt{prefix}[k]=\texttt{prefix}[k-1]+\texttt{arr}[k] -$$ - -For the example case, our prefix sum array looks like this: - -<center> - -| Index $i$ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | -| --- | --- | --- | --- | --- | --- | --- | --- | -| $\texttt{arr}[i]$ | 0 | 1 | 7 | 11 | 13 | 18 | 21 | - -</center> - -Now, when we want to query for the sum of the elements of $\texttt{arr}$ between (1-indexed) indices $a$ and $b$ inclusive, we can use the following formula: - -$$ -\sum_{i=L}^{R} \texttt{arr}[i] = \sum_{i=1}^{R} \texttt{arr}[i] - \sum_{i=1}^{L-1} \texttt{arr}[i] -$$ - -Using our definition of the elements in the prefix sum array, we have - -$$ -\sum_{i=L}^{R} \texttt{arr}[i]= \texttt{prefix}[R]-\texttt{prefix}[L-1] -$$ - -Since we are only querying two elements in the prefix sum array, we can calculate subarray sums in $O(1)$ per query, which is much better than the $O(N)$ per query that we had before. Now, after an $O(N)$ preprocessing to calculate the prefix sum array, each of the $Q$ queries takes $O(1)$ time. Thus, our total time complexity is $O(N+Q)$, which should now pass the time limit. - -Let's do an example query and find the subarray sum between indices $a = 2$ and $b = 5$, inclusive, in the 1-indexed $\texttt{arr}$. From looking at the original array, we see that this is - -$$ -\sum_{i=2}^{5} \texttt{arr}[i] = 6 + 4 + 2 + 5 = 17. -$$ - -Using prefix sums: -$$ -\texttt{prefix}[5] - \texttt{prefix}[1] = 18 - 1 = 17. -$$ - -There are also known as [partial sums](https://mathworld.wolfram.com/PartialSum.html). - -<problems-list problems={metadata.problems.cum} /> - -Now we'll look at some extensions. - -## Max Subarray Sum - -<problems-list problems={metadata.problems.maxsum} /> - -(Note: This problem has a solution known as Kadane's Algorithm. Please *don't* use that solution; try to solve it with prefix sums.) - -<spoiler title="Why are the two methods equivalent?"> - Consider the desired maximum subarray. As you go along from left to right, the prefix sum solution will mark the start of that subarray as the "current minimum prefix sum". Kadane's Algorithm, on the other hand, will set the current value to 0 at that point. As both solutions iterate through the array, they eventually find the right side of the maximum sum, and they find the answer to the problem at that location. In essence, Kadane's Algorithm stores the maximum sum of a subarray that ends at the current location (which is what the prefix sum solution calculates on each iteration), but it calculates this value greedily instead. -</spoiler> - -Extension: - -<problems-list problems={metadata.problems.maxsumext} /> - - -## 2D Prefix Sums - -<problems-list problems={metadata.problems.sample2} /> - -Now, what if we wanted to process $Q$ queries for the sum over a subrectangle of a $N$ rows by $M$ columns matrix in two dimensions? Let's assume both rows and columns are 1-indexed, and we use the following matrix as an example: - -(table) - -Naively, each sum query would then take $O(NM)$ time, for a total of $O(QNM)$. This is too slow. - -Let's take the following example region, which we want to sum: - -(table) - -Manually summing all the cells, we have a submatrix sum of $7+11+9+6+1+3 = 37$. - -The first logical optimization would be to do one-dimensional prefix sums of each row. Then, we'd have the following row-prefix sum matrix. The desired subarray sum of each row in our desired region is simply the green cell minus the red cell in that respective row. We do this for each row, to get $(28-1) + (14-4) = 37$. - -(table) - -Now, if we wanted to find a submatrix sum, we could break up the submatrix into a subarray for each row, and then add their sums, which would be calculated using the prefix sums method described earlier. Since the matrix has $N$ rows, the time complexity of this is $O(QN)$. This is better, but still usually not fast enough. - -To do better, we can do two-dimensional prefix sums. In our two dimensional prefix sum array, we have -$$ -\texttt{prefix}[a][b]=\sum_{i=1}^{a} \sum_{j=1}^{b} \texttt{arr}[i][j] -$$ - -This can be calculated as follows for row index $1 \leq i \leq n$ and column index $1 \leq j \leq m$: -$$ -\texttt{prefix}[i][j] - = \texttt{prefix}[i-1][j] - + \texttt{prefix}[i][j-1] - - \texttt{prefix}[i-1][j-1] - + \texttt{arr}[i][j] -$$ - -The submatrix sum between rows $a$ and $A$ and columns $b$ and $B$, can thus be expressed as follows: - -$$ -\sum_{i=a}^{A} \sum_{j=b}^{B} \texttt{arr}[i][j]=\texttt{prefix}[A][B] - - \texttt{prefix}[a-1][B] - - \texttt{prefix}[A][b-1] - + \texttt{prefix}[a-1][b-1] -$$ - -Summing the blue region from above using the 2d prefix sums method, we add the value of the green square, subtract the values of the red squares, and then add the value of the gray square. In this example, we have $65-23-6+1 = 37$, as expected. - -(table) - -Since no matter the size of the submatrix we are summing, we only need to access 4 values of the 2d prefix sum array, this runs in $O(1)$ per query after an $O(NM)$ preprocessing. This is fast enough. - -<problems-list problems={metadata.problems.cum2} /> - -## Prefix Minimum, XOR, etc. - -Similar to prefix sums, you can also take prefix minimum or maximum; but *you cannot* answer min queries over an arbitrary range with prefix minimum. (This is because minimum doesn't have an inverse operation, the way subtraction is to addition.) On the other hand, XOR is its own inverse operation, meaning that the XOR of any number with itself is zero. - -<problems-list problems={metadata.problems.related} /> - -## A More Complex Application - -Instead of storing just the values themselves, you can also take a prefix sum over $i\cdot a_i$, or $10^i \cdot a_i$, for instance. Some math is usually helpful for these problems; don't be afraid to get dirty with algebra! - -For instance, let's see how to quickly answer the following type of query: - -Find $1\cdot a_l+2\cdot a_{l+1}+3\cdot a_{l+2}+\cdots+(r-l+1)\cdot a_{r}$. - -First, define the following: -$$ -\texttt{ps}[i] = a_1+a_2+a_3+a_4+\cdots+a_i -$$ -$$ -\texttt{ips}[i] = 1\cdot a_1+2\cdot a_2+\cdots+i\cdot a_i -$$ - -Then, we have: -$$ -l\cdot a_l + (l+1) \cdot a_{l+1} + \cdots + r \cdot a_r = \texttt{ips}[r]-\texttt{ips}[l-1] -$$ -$$ -(l-1) \cdot a_l + (l-1) \cdot a_{l+1} + \cdot + (l-1) \cdot a_r = (l-1)(\texttt{ps}[r]-\texttt{ps}[l-1]) -$$ - -And so, -$$ -1\cdot a_l + 2 \cdot a_{l+1} + \cdots + (r-l+1) \cdot a_r = \texttt{ips}[r]-\texttt{ips}[l-1]-(l-1)(\texttt{ps}[r]-\texttt{ps}[l-1]) -$$ - -Which is what we were looking for! - -<problems-list problems={metadata.problems.complex} />
\ No newline at end of file diff --git a/content/4_Silver/Sorting_Cpp.mdx b/content/4_Silver/Sorting_Cpp.mdx deleted file mode 100644 index 6c7eb0f..0000000 --- a/content/4_Silver/Sorting_Cpp.mdx +++ /dev/null @@ -1,305 +0,0 @@ ---- -id: sorting-cpp -title: "More on Sorting in C++" -author: Siyong Huang, Michael Cao, Darren Yao, Benjamin Qi -prerequisites: - - Bronze - Introduction to Graphs - - Silver - Sorting with Custom Comparators -description: More information about sorting with custom comparators in C++. Some overlap with the previous module. ---- - -## Additional Reading - - - [fushar (C++)](http://fusharblog.com/3-ways-to-define-comparison-functions-in-cpp/) - -## Example: [Wormhole Sort (Silver)](http://www.usaco.org/index.php?page=viewproblem2&cpid=992) - -There are multiple ways to solve this problem, but all of them start by sorting the edges in nondecreasing order of weight. If we only stored the edge weights and sorted them, we would have a sorted list of edge weights, but it would be impossible to tell which weights corresponded to which edges. However, if we create a class representing the edges and define a custom comparator to sort them by weight, we can sort the edges in ascending order while also keeping track of their endpoints. - -## Comparators for Sorting - -There are 2 main ways to have a custom comparator in c++. - -### Overloading Operator - -[StackOverflow: Why const T&?](https://stackoverflow.com/questions/11805322/why-should-i-use-const-t-instead-of-const-t-or-t) - - - Pro: - - This is the easiest to implement - - Easy to work with STL - - Con: - - Only works for objects (not primitives) - - Only supports two types of comparisons (less than (<) and greater than (>)) - -<!-- Tested --> -```cpp -#include <bits/stdc++.h> -using namespace std; -int randint(int low, int high) {return low+rand()%(high-low);} - -struct Foo -{ - int Bar; - Foo(int _Bar=-1):Bar(_Bar){} - bool operator < (const Foo& foo2) const {return Bar < foo2.Bar;} -}; -const int N = 8; -int main() -{ - srand(69); - Foo a[N]; - for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); - sort(a, a+N); - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); -} - -/** -Output: -(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0) -(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18) -*/ -``` - -In the context of Wormhole Sort (uses [friend](https://www.geeksforgeeks.org/friend-class-function-cpp/)): - -```cpp -#include <bits/stdc++.h> -using namespace std; - -struct Edge { - int a,b,w; - friend bool operator<(const Edge& x, const Edge& y) { return x.w < y.w; } -}; // a different way to write less than - -int main() { - int M = 4; - vector<Edge> v; - for (int i = 0; i < M; ++i) { - int a,b,w; cin >> a >> b >> w; - v.push_back({a,b,w}); - } - sort(begin(v),end(v)); - for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; -} - -/* -Input: -1 2 9 -1 3 7 -2 3 10 -2 4 3 -*/ - -/* -Output: -2 4 3 -1 3 7 -1 2 9 -2 3 10 -*/ -``` - -### Function - - - Pro: - - Works for both objects and primitives - - Supports many different comparators for the same object - - Con: - - More difficult to implement - - Extra care needs to be taken to support STL - -We can also use [lambda expressions](https://www.geeksforgeeks.org/lambda-expression-in-c/) in C++11 or above. - -<!-- Tested --> - -```cpp -#include <bits/stdc++.h> -using namespace std; -int randint(int low, int high) {return low+rand()%(high-low);} - -struct Foo -{ - int Bar; - Foo(int _Bar=-1):Bar(_Bar){} -}; -const int N = 8; -Foo a[N]; -bool cmp1(Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;} -function<bool(Foo,Foo)> cmp2 = [](Foo foo1, Foo foo2) {return foo1.Bar < foo2.Bar;}; // lambda expression -// bool(Foo,Foo) means that the function takes in two parameters of type Foo and returns bool -// "function<bool(Foo,Foo)>"" can be replaced with "auto" - -int main() -{ - srand(69); - - printf("--- Method 1 ---\n"); - for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); - sort(a, a+N, [](Foo foo1, Foo foo2){return foo1.Bar<foo2.Bar;}); // lambda expression - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); - - printf("--- Method 2 ---\n"); - for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); - sort(a, a+N, cmp1); - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); - - printf("--- Method 3 ---\n"); - for(int i=0;i<N;++i) a[i] = Foo(randint(0, 20)); - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); - sort(a, a+N, cmp2); - for(int i=0;i<N;++i) printf("(Foo: %2d) ", a[i].Bar); printf("\n"); -} -``` - -Output: - -``` ---- Method 1 --- -(Foo: 3) (Foo: 18) (Foo: 13) (Foo: 5) (Foo: 18) (Foo: 3) (Foo: 15) (Foo: 0) -(Foo: 0) (Foo: 3) (Foo: 3) (Foo: 5) (Foo: 13) (Foo: 15) (Foo: 18) (Foo: 18) ---- Method 2 --- -(Foo: 5) (Foo: 13) (Foo: 18) (Foo: 0) (Foo: 9) (Foo: 4) (Foo: 2) (Foo: 15) -(Foo: 0) (Foo: 2) (Foo: 4) (Foo: 5) (Foo: 9) (Foo: 13) (Foo: 15) (Foo: 18) ---- Method 3 --- -(Foo: 1) (Foo: 1) (Foo: 18) (Foo: 0) (Foo: 11) (Foo: 12) (Foo: 1) (Foo: 5) -(Foo: 0) (Foo: 1) (Foo: 1) (Foo: 1) (Foo: 5) (Foo: 11) (Foo: 12) (Foo: 18) -``` - -In the context of Wormhole Sort: - -```cpp -#include <bits/stdc++.h> -using namespace std; - -struct Edge { int a,b,w; }; - -int main() { - int M = 4; - vector<Edge> v; - for (int i = 0; i < M; ++i) { - int a,b,w; cin >> a >> b >> w; - v.push_back({a,b,w}); - } - sort(begin(v),end(v),[](const Edge& x, const Edge& y) { return x.w < y.w; }); - for (Edge e: v) cout << e.a << " " << e.b << " " << e.w << "\n"; -} -``` - -### Comparators for STL - -Operator overloading works as expected for using in STL. If you are sorting elements in reverse order, you can use the STL [greater](https://en.cppreference.com/w/cpp/utility/functional/greater) comparator instead. - -For function comparators, some extra care needs to be taken: - -<!-- Reasonably Tested --> -```cpp -struct foo -{ - //members -}; -auto cmp1 = [](const foo& a, const foo& b){return /*comparator function*/;}; -//This way is shorter to write, but don't forget to pass the comparator as a parameter in the constructor! -//If you need to create an array of the objects, I would either use pointers of the method shown below -set<foo, decltype(cmp1)> Set1(cmp1); -priority_queue<foo, vector<foo>, decltype(cmp1)> pq1(cmp1); -//Side Note: priority queue is sorted in REVERSE order (largest elements are first) -map<foo, bar, decltype(cmp1)> Map1(cmp1); - - -struct cmp2 -{ - bool operator () (const foo& a, const foo& b){return /*comparator function*/;} -}; -//Here you do not need to pass the comparator as a parameter -//This makes it easier to create arrays of stl objects -set<foo, cmp2> Set2; -priority_queue<foo, vector<foo>, cmp2> pq2; -map<foo, bar, cmp2> Map2; -set<foo, cmp2> Set2b[100];//array of sets with the comparator -``` - -### Example of Comparators for Primitives - -Since you cannot overload operators for primitives, you must use custom comparators. - -<!-- Tested --> -```cpp -#include <bits/stdc++.h> -using namespace std; - -const int N = 8; -int a[N], b[N] = {4,8,2,3,4,1,2,4}; -int main() -{ - printf("--- Comparator 1 ---\n"); - iota(a, a+N, 0); - sort(a, a+N, greater<int>()); - //sort a in decreasing order - for(int i=0;i<N;++i) printf("a[%d] = %d\n", i, a[i]); - printf("--- Comparator 2 ---\n"); - iota(a, a+N, 0); - sort(a, a+N, [](int x, int y){return b[x]<b[y]||(b[x]==b[y]&&x>y);}); - //sort a by increasing values of b[i], breaking ties by decreasing index - for(int i=0;i<N;++i) printf("a[%d] = %d: b[%d] = %d\n", i, a[i], a[i], b[a[i]]); -} - -/* -Output: ---- Comparator 1 --- -a[0] = 7 -a[1] = 6 -a[2] = 5 -a[3] = 4 -a[4] = 3 -a[5] = 2 -a[6] = 1 -a[7] = 0 ---- Comparator 2 --- -a[0] = 5: b[5] = 1 -a[1] = 6: b[6] = 2 -a[2] = 2: b[2] = 2 -a[3] = 3: b[3] = 3 -a[4] = 7: b[7] = 4 -a[5] = 4: b[4] = 4 -a[6] = 0: b[0] = 4 -a[7] = 1: b[1] = 8 -*/ -``` - -Comments: Comparator 1 sorts array $a$ in decreasing order. Comparator 2 sorts array $a$ in increasing $b[a[i]]$ value, breaking ties by placing the greater index first. - -## Sorting Pairs - -An alternative way of representing custom objects is with the data structure `pair<int, int>`. In the above example, instead of creating a `struct`, we can simply declare an array of pairs. The sort function automatically uses the first element of the pair for comparison and the second element as a secondary point of comparison: - -```cpp -pair<int, int> arr[N]; - -int main() { - sort(arr, arr+N); -} -``` - -For wormhole sort, there is no need to create a custom comparator: - -```cpp -#include <bits/stdc++.h> -using namespace std; - -#define f first -#define s second - -int main() { - int M = 4; - vector<pair<int,pair<int,int>>> v; - for (int i = 0; i < M; ++i) { - int a,b,w; cin >> a >> b >> w; - v.push_back({w,{a,b}}); - } - sort(begin(v),end(v)); - for (auto e: v) cout << e.s.f << " " << e.s.s << " " << e.f << "\n"; -} -``` diff --git a/content/4_Silver/Sorting_Custom.mdx b/content/4_Silver/Sorting_Custom.mdx deleted file mode 100644 index ed7d6cf..0000000 --- a/content/4_Silver/Sorting_Custom.mdx +++ /dev/null @@ -1,350 +0,0 @@ ---- -id: sorting-custom -title: "Sorting with Custom Comparators" -author: Darren Yao, Siyong Huang -prerequisites: - - Silver - Introduction to Sorting -description: Both Java and C++ have built-in functions for sorting. However, if we use custom objects, or if we want to sort elements in a different order, then we'll need to use a custom comparator. ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("Silver", "Lifeguards", "786", "Easy", false, [], "sort endpoints of intervals"), - 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("Silver", "Meetings", "967", "Very Hard", false, [], ""), - ], - } -}; - -<info-block title="Pro Tip"> - -Especially important for competitors using Java. - -</info-block> - -## Comparators - -Normally, sorting functions rely on moving objects with a lower value in front of objects with a higher value if sorting in ascending order, and vice versa if in descending order. This is done through comparing two objects at a time. - -### C++ - -What a comparator does is compare two objects as follows, based on our comparison criteria: - - - If object $x$ is less than object $y$, return `true` - - If object $x$ is greater than or equal to object $y$, return `false` - -Essentially, the comparator determines whether object $x$ belongs to the left of object $y$ in a sorted ordering. A comparator **must** return false for two identical objects (not doing so results in undefined behavior and potentially RTE). - -In addition to returning the correct answer, comparators should also satisfy the following conditions: - - - The function must be consistent with respect to reversing the order of the arguments: if $x \neq y$ and `compare(x, y)`is positive, then `compare(y, x)` should be negative and vice versa - - The function must be transitive. If `compare(x, y)` is true and `compare(y, z)` is true, then `compare(x, z)` should also be true. If the first two compare functions both return `false`, the third must also return `false`. - -A generic way of implementing a custom comparator is to define a function. For our example, we'll use a `struct` of a Person that contains a person's height and weight, and sort in ascending order by height. A `struct` is essentially a user-defined data structure: - -```cpp -struct Person { - int height; - int weight; -}; -int main() { - Person p; - p.height = 60; // assigns 60 to the height of p - p.weight = 100; // assigns 100 to the weight of p -} -``` - -Let's say we have an array `Person arr[N]`. To sort the array, we need to make custom comparator which will be a function, and then pass the function as a parameter into the build-in sort function: - -```cpp -bool cmp(Person a, Person b) { - return a.height < b.height; -} - -int main() { - sort(arr, arr+N, cmp); // sorts the array in ascending order by height -} -``` - -If we instead wanted to sort in descending order, this is also very simple. Instead of the `cmp` function returning `return a.height < b.height;`, it should do `return a.height > b.height;`. - -### Java - -What a `Comparator` does is compare two objects as follows, based on our comparison criteria: - -- If object $x$ is less than object $y$, return a negative number. -- If object $x$ is greater than object $y$, return a positive number. -- If object $x$ is equal to object $y$, return 0. - -In addition to returning the correct number, comparators should also satisfy the following conditions: -- The function must be consistent with respect to reversing the order of the arguments: if $compare(x, y)$ is positive, then $compare(y, x)$ should be negative and vice versa. -- The function must be transitive. If $compare(x, y) > 0$ and $compare(y, z) > 0$, then $compare(x, z) > 0$. Same applies if the compare functions return negative numbers. -- Equality must be consistent. If $compare(x, y) = 0$, then $compare(x, z)$ and $compare(y, z)$ must both be positive, both negative, or both zero. Note that they don't have to be equal, they just need to have the same sign. - -Java has default functions for comparing `int`, `long`, `double` types. The `Integer.compare()`, `Long.compare()`, and `Double.compare()` functions take two arguments $x$ and $y$ and compare them as described above. - -Now, there are two ways of implementing this in Java: `Comparable`, and `Comparator`. They essentially serve the same purpose, but `Comparable` is generally easier and shorter to code. `Comparable` is a function implemented within the class containing the custom object, while `Comparator` is its own class. For our example, we'll use a `Person` class that contains a person's height and weight, and sort in ascending order by height. - -If we use `Comparable`, we'll need to put `implements Comparable<Person>` into the heading of the class. Furthermore, we'll need to implement the `compareTo` method. Essentially, `compareTo(x)` is the `compare` function that we described above, with the object itself as the first argument, or `compare(self, x)`. - -```java -static class Person implements Comparable<Person>{ - int height, weight; - public Person(int h, int w){ - height = h; weight = w; - } - public int compareTo(Person p){ - return Integer.compare(height, p.height); - } -} -``` - -When using Comparable, we can just call `Arrays.sort(arr)` or `Collections.sort(list)` on the array or list as usual. - -If instead we choose to use `Comparator`, we'll need to declare a second `Comparator` class, and then implement that: - -```java -static class Person{ - int height, weight; - public Person(int h, int w){ - height = h; weight = w; - } -} -static class Comp implements Comparator<Person>{ - public int compare(Person a, Person b){ - return Integer.compare(a.height, b.height); - } -} -``` - -When using `Comparator`, the syntax for using the built-in sorting function requires a second argument: `Arrays.sort(arr, new Comp())`, or `Collections.sort(list, new Comp())`. - -If we instead wanted to sort in descending order, this is also very simple. Instead of the comparing function returning `Integer.compare(x, y)` of the arguments, it should instead return `-Integer.compare(x, y)`. - -### Python - -There are 3 main ways to create a custom comparator in python - -#### 1) Operator Overloading - -<!-- Tested --> -```py -import random -class Foo: - def __init__(self, _Bar): self.Bar = _Bar - def __str__(self): return "Foo({})".format(self.Bar) - def __lt__(self, o): # lt means less than - return self.Bar < o.Bar - -a = [] -for i in range(8): - a.append(Foo(random.randint(1, 10))) -print(*a) -print(*sorted(a)) -``` - -Output: - -``` -Foo(0) Foo(1) Foo(2) Foo(1) Foo(9) Foo(5) Foo(5) Foo(8) -Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9) -``` - -#### 2) Remapping Key - - - This method maps an object to another comparable datatype with which to be sorted. In this case, `Foo` is sorted by the sum of its members `x` and `y`. - - -<!-- Tested --> -```py -import random -class Foo: - def __init__(self, _Bar, _Baz): self.Bar,self.Baz = _Bar,_Baz - def __str__(self): return "Foo({},{})".format(self.Bar, self.Baz) - -a = [] -for i in range(8): - a.append(Foo(random.randint(1, 9)*10, random.randint(1, 9))) -print(*a) - -print(*sorted(a, key=lambda foo: foo.Bar+foo.Baz)) -def key(foo): - return foo.Bar + foo.Baz -print(*sorted(a, key=key)) -``` - -Output: - -``` -Foo(10,2) Foo(30,2) Foo(60,6) Foo(90,7) Foo(80,7) Foo(80,9) Foo(60,9) Foo(90,8) -Foo(10,2) Foo(30,2) Foo(60,6) Foo(60,9) Foo(80,7) Foo(80,9) Foo(90,7) Foo(90,8) -Foo(10,2) Foo(30,2) Foo(60,6) Foo(60,9) Foo(80,7) Foo(80,9) Foo(90,7) Foo(90,8) -``` - -#### 3) Function / Lambda - - - This method defines how to compare two elements represented by an integer - - Positive: First term is greater than the second term - - Zero: First term and second term are equal - - Negative: First term is less than the second term - -Note how the comparator must be converted to a `key`. - -<!-- Tested --> - -```py -import random -from functools import cmp_to_key -class Foo: - def __init__(self, _Bar): self.Bar = _Bar - def __str__(self): return "Foo({})".format(self.Bar) - -a = [] -for i in range(8): - a.append(Foo(random.randint(0, 9))) -print(*a) - -print(*sorted(a, key=cmp_to_key(lambda foo1, foo2: foo1.Bar - foo2.Bar))) -def cmp(foo1, foo2): - return foo1.Bar - foo2.Bar -print(*sorted(a, key=cmp_to_key(cmp))) -``` - -Output: - -``` -Foo(0) Foo(1) Foo(2) Foo(1) Foo(9) Foo(5) Foo(5) Foo(8) -Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9) -Foo(0) Foo(1) Foo(1) Foo(2) Foo(5) Foo(5) Foo(8) Foo(9) -``` - - -## Sorting by Multiple Criteria - -Now, suppose we wanted to sort a list of `Person`s in ascending order, primarily by height and secondarily by weight. We can do this quite similarly to how we handled sorting by one criterion earlier. What the comparator function needs to do is to compare the weights if the heights are equal, and otherwise compare heights, as that's the primary sorting criterion. - -### C++ - - -```cpp -bool cmp(Person a, Person b) { - if(a.height == b.height) { - return a.weight < b.weight; - } - return a.height < b.height; -} - -int main() { - sort(arr, arr+N, cmp); // sorts the array in ascending order by height and then weight if the heights are equal -} -``` - -Sorting with an arbitrary number of criteria is done similarly. - -### Java - -```java -static class Person implements Comparable<Person>{ - int height, weight; - public Person(int h, int w){ - height = h; weight = w; - } - public int compareTo(Person p){ - if(height == p.height){ - return Integer.compare(weight, p.weight); - } else { - return Integer.compare(height, p.height); - } - } -} -``` - -Sorting with an arbitrary number of criteria is done similarly. - -An alternative way of representing custom objects is with arrays. Instead of using a custom object to store data about each person, we can simply use `int[]`, where each `int` array is of size 2, and stores pairs of {height, weight}, probably in the form of a list like `ArrayList<int[]>`. Since arrays aren't objects in the usual sense, we need to use `Comparator`. Example for sorting by the same two criteria as above: - -```java -static class Comp implements Comparator<int[]>{ - public int compare(int[] a, int[] b){ - if(a[0] == b[0]){ - return Integer.compare(a[1], b[1]); - } else { - return Integer.compare(a[0], b[0]); - } - } -} -``` - -I don't recommend using arrays to represent objects mostly because it's confusing, but it's worth noting that some competitors do this. - -### Python - -Operator Overloading can be used - -<!-- Tested --> -```py -import random -class Foo: - def __init__(self, _Bar, _Baz): self.Bar,self.Baz = _Bar,_Baz - def __str__(self): return "Foo({},{})".format(self.Bar, self.Baz) - def __lt__(self, o): # sort by increasing Bar, breaking ties by decreasing Baz - if self.Bar != o.Bar: return self.Bar < o.Bar - if self.Baz != o.Baz: return self.Baz > o.Baz - return False - -a = [] -for i in range(8): - a.append(Foo(random.randint(1, 9), random.randint(1, 9))) -print(*a) -print(*sorted(a)) -``` - -Output: - -``` -Foo(1,2) Foo(3,2) Foo(6,6) Foo(9,7) Foo(8,7) Foo(8,9) Foo(6,9) Foo(9,8) -Foo(1,2) Foo(3,2) Foo(6,9) Foo(6,6) Foo(8,9) Foo(8,7) Foo(9,8) Foo(9,7) -``` - -A custom comparator works as well - - Lambdas aren't shown here because they are typically used as one-liners - -```py -import random -from functools import cmp_to_key - -class Foo: - def __init__(self, _Bar, _Baz): self.Bar,self.Baz = _Bar,_Baz - def __str__(self): return "Foo({},{})".format(self.Bar, self.Baz) - -a = [] -for i in range(8): - a.append(Foo(random.randint(1, 9), random.randint(1, 9))) -print(*a) - -def cmp(foo1, foo2): # Increasing Bar, breaking ties with decreasing Baz - if foo1.Bar != foo2.Bar: return -1 if foo1.Bar < foo2.Bar else 1 - if foo1.Baz != foo2.Baz: return -1 if foo1.Baz > foo2.Baz else 1 - return 0 -print(*sorted(a, key=cmp_to_key(cmp))) - -# Python automatically sorts tuples in increasing order with priority to the leftmost element -# You can sort objects by its mapping to a tuple of its elements -# The following sorts Foo by increasing Bar values, breaking ties with increasing Baz value -print(*sorted(a, key=lambda foo: (foo.Bar, foo.Baz))) -``` -Output: -``` -Foo(1,2) Foo(3,2) Foo(6,6) Foo(9,7) Foo(8,7) Foo(8,9) Foo(6,9) Foo(9,8) -Foo(1,2) Foo(3,2) Foo(6,9) Foo(6,6) Foo(8,9) Foo(8,7) Foo(9,8) Foo(9,7) -Foo(1,2) Foo(3,2) Foo(6,6) Foo(6,9) Foo(8,7) Foo(8,9) Foo(9,7) Foo(9,8) -``` - -## Problems - -<problems-list problems={metadata.problems.general} /> diff --git a/content/4_Silver/Stacks_Queues.mdx b/content/4_Silver/Stacks_Queues.mdx deleted file mode 100644 index 76a8b3a..0000000 --- a/content/4_Silver/Stacks_Queues.mdx +++ /dev/null @@ -1,206 +0,0 @@ ---- -id: stacks-queues -title: Stacks & Queues -author: Darren Yao -description: -prerequisites: - - Bronze - Data Structures ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - 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"],"") - ], - } -}; - -## Additional Reading - - - CPH 4.5 - - [PAPS 3.2, 3.3, 3.4, 6.2, 6.3, 6.5](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - -## [Stacks](http://www.cplusplus.com/reference/stack/stack/) - -A stack is a **Last In First Out** (LIFO) data structure that supports three operations, all in $O(1)$ time. Think of it like a real-world stack of papers (or cards). - -### C++ - - - `push`: adds an element to the top of the stack - - `pop`: removes an element from the top of the stack - - `top`: retrieves the element at the top without removing it - -```cpp -stack<int> s; -s.push(1); // [1] -s.push(13); // [1, 13] -s.push(7); // [1, 13, 7] -cout << s.top() << endl; // 7 -s.pop(); // [1, 13] -cout << s.size() << endl; // 2 -``` - -### Java - - - `push`: adds an element to the top of the stack - - `pop`: removes an element from the top of the stack - - `peek`: retrieves the element at the top without removing it - -```java -Stack<Integer> s = new Stack<Integer>(); -s.push(1); // [1] -s.push(13); // [1, 13] -s.push(7); // [1, 13, 7] -System.out.println(s.peek()); // 7 -s.pop(); // [1, 13] -System.out.println(s.size()); // 2 -``` - -## [Queues](http://www.cplusplus.com/reference/queue/queue/) - -A queue is a First In First Out (FIFO) data structure that supports three operations, all in $O(1)$ time. - -### C++ - - - `push`: insertion at the back of the queue - - `pop`, deletion from the front of the queue - - `front`: which retrieves the element at the front without removing it. - -```cpp -queue<int> q; -q.push(1); // [1] -q.push(3); // [3, 1] -q.push(4); // [4, 3, 1] -q.pop(); // [4, 3] -cout << q.front() << endl; // 3 -``` - -### Java - - - `add`: insertion at the back of the queue - - `poll`: deletion from the front of the queue - - `peek`, which retrieves the element at the front without removing it - -Java doesn't actually have a `Queue` class; it's only an interface. The most commonly used implementation is the `LinkedList`, declared as follows: - -```java -Queue<Integer> q = new LinkedList<Integer>(); -q.add(1); // [1] -q.add(3); // [3, 1] -q.add(4); // [4, 3, 1] -q.poll(); // [4, 3] -System.out.println(q.peek()); // 3 -``` - -## [Deques](http://www.cplusplus.com/reference/deque/deque/) - -A deque (usually pronounced "deck") stands for double ended queue and is a combination of a stack and a queue, in that it supports $O(1)$ insertions and deletions from both the front and the back of the deque. Not very common in Bronze / Silver. - -### C++ - -The four methods for adding and removing are `push_back`, `pop_back`, `push_front`, and `pop_front`. - -```cpp -deque<int> d; -d.push_front(3); // [3] -d.push_front(4); // [4, 3] -d.push_back(7); // [4, 3, 7] -d.pop_front(); // [3, 7] -d.push_front(1); // [1, 3, 7] -d.pop_back(); // [1, 3] -``` - -### Java - -In Java, the deque class is called `ArrayDeque`. The four methods for adding and removing are `addFirst` , `removeFirst`, `addLast`, and `removeLast`. - -```java -ArrayDeque<Integer> deque = new ArrayDeque<Integer>(); -deque.addFirst(3); // [3] -deque.addFirst(4); // [4, 3] -deque.addLast(7); // [4, 3, 7] -deque.removeFirst(); // [3, 7] -deque.addFirst(1); // [1, 3, 7] -deque.removeLast(); // [1, 3] -``` - -## [Priority Queues](http://www.cplusplus.com/reference/queue/priority_queue/) - -A priority queue supports the following operations: insertion of elements, deletion of the element considered highest priority, and retrieval of the highest priority element, all in $O(\log n)$ time according to the number of elements in the priority queue. Priority is based on a comparator function. The priority queue is one of the most important data structures in competitive programming, so make sure you understand how and when to use it. - -### C++ - -```cpp -priority_queue<int> pq; -pq.push(7); // [7] -pq.push(2); // [2, 7] -pq.push(1); // [1, 2, 7] -pq.push(5); // [1, 2, 5, 7] -cout << pq.top() << endl; // 7 -pq.pop(); // [1, 2, 5] -pq.pop(); // [1, 2] -pq.push(6); // [1, 2, 6] -``` - -### Java - -In Java, we delete and retrieve the element of **lowest** priority. - -```java -PriorityQueue<Integer> pq = new PriorityQueue<Integer>(); -pq.add(7); // [7] -pq.add(2); // [7, 2] -pq.add(1); // [7, 2, 1] -pq.add(5); // [7, 5, 2, 1] -System.out.println(pq.peek()); // 1 -pq.poll(); // [7, 5, 2] -pq.poll(); // [7, 5] -pq.add(6); // [7, 6, 5] -``` - -## General Problems - -## Problems - -(actually go through these and check ...) - -### Stack - - - UVa 00514 - Rails - - UVa 00732 - Anagram by Stack - - UVa 01062 - Containers - -### Queue / Deque - - - UVa 10172 - The Lonesome Cargo - - UVa 10901 - Ferry Loading III - - UVa 11034 - Ferry Loading IV - -## Monotonic Stack - -Consider the following problem: - -> Given an array, $a$, of $N$ ($1 \le N \le 10^5$) integers, for every index $i$, find the rightmost index $j$ such that $j < i$ and $a_i > a_j$. - -To solve this problem, let's store a stack of pairs of `<value, index>` and iterate over the array from left to right. For some index $i$, we will compute $ans_i$, the rightmost index for $i$, as follows: - -- Keep popping the top element off the stack as long as $value \ge a_i$. This is because we know that the pair containing $value$ will never be the solution to any index $j > i$, since $a_i$ is less than or equal to than $value$ and has an index further to the right. -- If $value < a_i$, set $ans[i]$ to $index$, because a stack stores the most recently added values first (or in this case, the rightmost ones), $index$ will contain the rightmost value which is less than $a_i$. Then, pop the top element off the stack, because $index$ can't be the solution for two elements. - -The stack we used is called a "monotonic stack" because we keep popping off the top element of the stack which maintains it's monotonicity (the same property needed for algorithms like binary search) because the elements in the stack are increasing. - -### Further Reading - - - CPH 8.2 (Nearest Smaller Element) - - [cp-algo - Min Stack](https://cp-algorithms.com/data_structures/stack_queue_modification.html) - - [Medium - Monotonic Stack](https://medium.com/@vishnuvardhan623/monotonic-stack-e9dcc4fa8c3e) - -### Problems - -<problems-list problems={metadata.problems.general} /> - -(add more once codeforces comes up) diff --git a/content/5_Gold/DP.mdx b/content/5_Gold/DP.mdx deleted file mode 100644 index 4019d66..0000000 --- a/content/5_Gold/DP.mdx +++ /dev/null @@ -1,107 +0,0 @@ ---- -id: dp -title: "Introduction to Dynamic Programming" -author: Michael Cao -prerequisites: - - Recursion - - Silver - Prefix Sums -description: Speeding up naive solutions with memoization. -frequency: 4 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - usacoPast: [ - new Problem("Gold", "Hoof Paper Scissors", "694", "Easy", false, ["DP"], "dp[first i games][# changes][last gesture] -> max games won"), - new Problem("Gold", "Time is Mooney", "993", "Easy", true, ["DP", "Graphs"], "dp[time][city] -> money"), - new Problem("Gold", "Teamwork", "863", "Easy", false, ["DP"], "let dp[i] -> max sum of skill levels for the first i cows, then precompute subarray maximums"), - new Problem("Gold", "Snakes", "945", "Easy", false, ["DP"], "dp[i][j] -> minimum sum of net sizes needed to catch m snakes with k changes, precompute maximums"), - new Problem("Gold", "Circular Barn Revisited", "622", "Normal", true, ["DP", "Brute Force"], "brute force starting location, then do dp[first i positions][number of doors used][position of last door] -> minimum distance cows need to travel"), - new Problem("Gold", "Taming the Herd", "815", "Normal", false, ["DP"], "dp[consider first i entries only][last breakout in first i occurs at j][k breakouts among first i entries] -> # changes"), - new Problem("Gold", "Mortal Cowmbat", "815", "Normal", true, ["DP", "Prefix Sums", "All Pairs Shortest Path"], "dp[first i letters form valid combo][last letter] -> time, Floyd Warshall on alphabet, then use prefix sums to speed up transitions."), - new Problem("Gold", "Stamp Painting", "791", "Hard", false, ["DP"], "must be K consectutive of same color, complimetary counting for dp[up to position i][number of consecutive] -> number of ways, find closed form to reduce runtime"), - ], - knapsack: [ - new Problem("CSES", "Unordered Coin Change", "1635", "Intro", true, ["DP", "Knapsack"], "dp[first i coins][sum] = number of ways, order of loops is capacity then coins, remember to take MOD after every computation"), - new Problem("CSES", "Ordered Coin Change", "1636", "Intro", true, ["DP", "Knapsack"], "dp[first i coins][sum] = number of ways, order of loops is coins then capacity, remember to take MOD after every computation"), - new Problem("CSES", "Minimum Coins", "1634", "Intro", true, ["DP", "Knapsack"], "dp[first i coins][sum] = minimum number of coins needed"), - new Problem("Atcoder", "Knapsack 2", "https://atcoder.jp/contests/dp/tasks/dp_e", "Easy", false, ["DP", "Knapsack"], "maximum capacity is large, and sum of values is small, so switch the states. dp[first i items][sum of values] = minimum capacity needed to achieve this sum"), - new Problem("Gold", "Fruit Feast", "574", "Easy", false, ["DP", "Knapsack"], "dp[fullness] = whether you can achieve this fullness"), - new Problem("Gold", "Talent Show", "839", "Hard", false, ["DP", "Knapsack", "Binary Search", "Math"], "binary search on optimal ratio, then do knapsack on weight"), - new Problem("CF", "Round Subset", "http://codeforces.com/contest/837/problem/D", "Normal", false, ["DP", "Knapsack"], "dp[i][j][l] -> maximum amount of twos we can collect by checking first i numbers, taking j of them with total power of five equal to l"), - ], - pathsGrid: [ - new Problem("LC", "Longest Common Subsequence", "https://leetcode.com/problems/longest-common-subsequence/", "Intro", true, ["DP"], "dp[first i characters in first string][first j characters in second string] -> longest common subsequence, transition if s[i] = t[j] for strings s and t"), - new Problem("HackerRank", "Edit Distance", "https://www.hackerrank.com/contests/cse-830-homework-3/challenges/edit-distance", "Intro", true, ["DP"], "dp[first i characters in first string][first j characters in second string] -> edit distance"), - new Problem("Atcoder", "Count Paths", "https://atcoder.jp/contests/dp/tasks/dp_h", "Intro", 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("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/", "Intro", true, ["DP"], "dp[i] = LIS up to i, use binary search to decrease runtime from quadratic"), - new Problem("Old Gold", "Cowjog", "496", "Easy", false, ["DP"], "direct applicationf of longest increasing subsequence"), - new Problem("Plat", "Sort It Out", "865", "Hard", false, ["DP"], "component of kth largest LIS, read editorial for more details"), - ], - } -} - -Dynamic Programming is an important algorithmic technique in competitive programming which extends to even the International Olympiad of Informatics. By storing results to overlapping subproblems, Dynamic Programming optimizes the redundant computation of naive recursive solutions, massively improving computational complexity. Although it is not too difficult to grasp the general ideas behind Dynamic Programming, or DP, the technique can be used in a diverse range of problems and is a must-know idea for competitors in the USACO gold division. - -<info-block title="Contest Tip"> - -Usually, at least one problem from every gold division contest involves some sort of DP! - -</info-block> - -## Tutorial - -The following tutorials serve as an introduction into the mindset of DP. - - - CPH 7 - - great introduction that covers most classical problems - - [Topcoder DP](https://www.topcoder.com/community/competitive-programming/tutorials/dynamic-programming-from-novice-to-advanced/) - - great for all skill levels - - [CPC.6](https://github.com/SuprDewd/T-414-AFLV/tree/master/06_dynamic_programming) - - examples with nonclassical problems - - [HackerRank DP](https://www.hackerrank.com/topics/dynamic-programming) - - also covers many classical problems - -Practice makes perfect. Start by doing some classical problems (try at least one of each), as these are **must know** DP problems. Each topic starts with direct applications of the classical problems, and then some interesting variations and USACO problems which utilize the ideas. Solutions for most problems (excluding USACO) can be found on Chapter 7 of CPH. - -<info-block title="Pro Tip"> - -Sometimes it's a good idea to write a slower polynomial-time solution and then optimize it to the desired complexity (say, write $O(N^2)$ first and then speed it up to $O(N)$). - -</info-block> - -## Knapsack - -<problems-list problems={metadata.problems.knapsack} /> - -## Paths in a Grid (and related) - -<problems-list problems={metadata.problems.pathsGrid} /> - -## Longest Increasing Subsequence - -<problems-list problems={metadata.problems.lis} /> - -## USACO Problems - -<problems-list problems={metadata.problems.usacoPast} /> - -Here are some other very good DP problemsets. DP is a very important topic, so you should do a lot of practice on it. - -## Other DP Problemsets - - - [**Atcoder DP Contest**](https://atcoder.jp/contests/dp/tasks) - - very good! - - [CSES DP Section](https://cses.fi/problemset/list/) - - also very good! - - [Codeforces DP Problem List](http://codeforces.com/blog/entry/325) - - diff --git a/content/5_Gold/DP_Trees.mdx b/content/5_Gold/DP_Trees.mdx deleted file mode 100644 index fa4220d..0000000 --- a/content/5_Gold/DP_Trees.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -id: dp-trees -title: "Dynamic Programming on Trees" -author: Michael Cao -prerequisites: - - Silver - Depth First Search - - Gold - Introduction to Dynamic Programming -description: What it sounds like. -frequency: 1 ---- - - -## Tutorial - - * [DP on Trees (Codeforces)](https://codeforces.com/blog/entry/20935) - * [NOI PDF (Philippines)](https://noi.ph/training/weekly/week5.pdf) - -## Problems - -<info-block title="Pro Tip"> - -Don't just dive into trying to figure out a DP state and transitions -- make some observations if you don't see any obvious DP solution! - -</info-block> - - - USACO - - [Gold - Barn Painting](http://www.usaco.org/index.php?page=viewproblem2&cpid=766) - - Similar to Independent Set - - [Gold - Delegation](http://usaco.org/index.php?page=viewproblem2&cpid=1019) - - Pretty much a greedy problem. - - [Plat - Delegation](http://www.usaco.org/index.php?page=viewproblem2&cpid=1020) - - Binary search on answer! - - Other - - [CSES - Tree Matching](https://cses.fi/problemset/task/1130) - - [Subtree](https://atcoder.jp/contests/dp/tasks/dp_v) - - [Independent Set](https://atcoder.jp/contests/dp/tasks/dp_p) - - [BOI - Cat In A Tree](https://cses.fi/file/a904421da451fbe1b60f96a27256832a011825dfd097ee40184d15878e837018/) - - Observations first. - - [COCI - Dzumbus](https://oj.uz/problem/view/COCI19_dzumbus) - - Solve child subtree by subtree. Runtime may be faster than you think. - - [Ostap and Tree](https://codeforces.com/problemset/problem/735/E) diff --git a/content/5_Gold/DSU.mdx b/content/5_Gold/DSU.mdx deleted file mode 100644 index 4836ddb..0000000 --- a/content/5_Gold/DSU.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -id: dsu -title: "Disjoint Set Union" -author: Benjamin Qi, Michael Cao -prerequisites: - - Silver - Depth First Search -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 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("YS", "Union Find", "unionfind", "Easy", false, []), - ], - general: [ - new Problem("Gold", "Mootube", "789", "Easy", false, [], "same as [CSES Road Construction](https://cses.fi/problemset/task/1676)"), - new Problem("Gold", "Closing the Farm", "646", "Easy", false, [], "similar to [CSES Network Breakdown](https://cses.fi/problemset/task/1677)"), - new Problem("Gold", "Favorite Colors", "1042", "Hard", false, [], ""), - ], - rollback: [ - new Problem("YS", "Persistent Union Find", "persistent_unionfind", "Normal", false, [], ""), - new Problem("CF", "Edu F - Extending Set of Points", "contest/1140/problem/F", "Hard", false, [], ""), - ], - } -}; - -<problems-list problems={metadata.problems.sample} /> - -## Tutorial - - - CPH 15.2 - - [PAPS 11.1](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - Chapter 10.6 of "An Introduction to the US Computing Olympiad" - - [CSAcademy Disjoint-Set](https://csacademy.com/lesson/disjoint_data_sets) - - [Topcoder Union Find](https://www.topcoder.com/community/data-science/data-science-tutorials/disjoint-set-data-structures/) - - [CPC.3](https://github.com/SuprDewd/T-414-AFLV/tree/master/03_data_structures) - -<optional-content title="DSU Complexity Proofs"> - - - [$\log^* n$](https://en.wikipedia.org/wiki/Proof_of_O(log*n)\_time_complexity\_of_union%E2%80%93find) - - [$\alpha (m,n)$](https://dl.acm.org/doi/pdf/10.1145/321879.321884) - -</optional-content> - -## Problems - -<problems-list problems={metadata.problems.general} /> - -## Rollback - -no path compression - -<problems-list problems={metadata.problems.rollback} /> - -https://judge.yosupo.jp/problem/dynamic_graph_vertex_add_component_sum
\ No newline at end of file diff --git a/content/5_Gold/Hashing.mdx b/content/5_Gold/Hashing.mdx deleted file mode 100644 index a1ecbee..0000000 --- a/content/5_Gold/Hashing.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -id: hashing -title: "Hashing" -author: Benjamin Qi -description: Quickly test equality of substrings with a small probability of failure. -frequency: 1 ---- - -## Tutorial - - - [PAPS 14.3](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - CPH 26.3 - - [cp-algorithms String Hashing](https://cp-algorithms.com/string/string-hashing.html) - - [Anti-Hash Tests](https://codeforces.com/blog/entry/60442) - - On CodeForces educational rounds in particular, make sure to use random bases. - -My implementation can be found [here](https://github.com/bqi343/USACO/blob/master/Implementations/content/strings%20(14)/Light/HashRange%20(14.2).h). It uses two bases rather than just one to decrease the probability that two random strings hash to the same value. As mentioned in the articles above, there is no need to calculate modular inverses. - -## Problems - - - USACO - - [Gold Cownomics](http://www.usaco.org/index.php?page=viewproblem2&cpid=741) - - Use two pointers; for a fixed $l$, keep extending $r$ to the right until the positions $l\ldots r$ explain spotiness. - - Hashing gives you a way to quickly check whether two substrings of different cow types are equal. So for a single $[l,r]$ pair you can check whether it works in $O(N\log N)$ time (and you only need to check $O(M)$ of these pairs in total). - - Actually, it's possible to pass $O(N^2M)$ (or even slower) solutions. - - [Gold Lightsout](http://www.usaco.org/index.php?page=viewproblem2&cpid=599) - - figure out whether this actually needs hashing? (check ...) - - Other (check ...) - - [Palindromic Partitions](https://csacademy.com/contest/ceoi-2017-day-2/task/palindromic-partitions/) - - [Liar](http://codeforces.com/problemset/problem/822/E) [](93) - - [Palindromic Characteristics](http://codeforces.com/problemset/problem/835/D) [](100) - - [Berland SU Computer Network](http://codeforces.com/contest/847/problem/L) [](142) diff --git a/content/5_Gold/Intro_NT.mdx b/content/5_Gold/Intro_NT.mdx deleted file mode 100644 index 4e8ded7..0000000 --- a/content/5_Gold/Intro_NT.mdx +++ /dev/null @@ -1,135 +0,0 @@ ---- -id: intro-nt -title: "Introductory Number Theory" -author: Darren Yao, Michael Cao -prerequisites: - - Gold - Introduction to Dynamic Programming -description: Divisibility and Modular Arithmetic -frequency: 1 ---- - -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, []), - ], - general: [ - 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$."), - ], - } -}; - -## Additional Resources - - - CPH 21.1 (Primes and factors), 21.2 (Modular arithmetic) - - [PAPS 16](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [CF CodeNCode: Number Theory Course](https://codeforces.com/blog/entry/77137) - -## Prime Factorization - -<problems-list problems={metadata.problems.sample} /> - -A number $a$ is called a **divisor** or a **factor** of a number $b$ if $b$ is divisible by $a$, which means that there exists some integer $k$ such that $b = ka$. Conventionally, $1$ and $n$ are considered divisors of $n$. A number $n > 1$ is **prime** if its only divisors are $1$ and $n$. Numbers greater than \(1\) that are not prime are **composite**. - -Every number has a unique **prime factorization**: a way of decomposing it into a product of primes, as follows: -$$ -n = {p_1}^{a_1} {p_2}^{a_2} \cdots {p_k}^{a_k} -$$ -where the $p_i$ are distinct primes and the $a_i$ are positive integers. - -Now, we will discuss how to find the prime factorization of an integer. - -(pseudocode) - -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$. - -(table) - -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\}$. - - -## GCD & LCM - -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} - a & b = 0 \\ - \gcd(b, a \bmod b) & b \neq 0 \\ -\end{cases} -$$ - -This algorithm is very easy to implement using a recursive function in Java, as follows: - -```java -public int gcd(int a, int b){ - 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)$. - -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)}. -$$ - -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, - -$$ -\gcd(a_1, a_2, a_3, a_4) = \gcd(a_1, \gcd(a_2, \gcd(a_3, a_4))). -$$ - -## 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: - -``` -todo no support for gather -$$ -\begin{gather*} - (a+b) \bmod m = (a \bmod m + b \bmod m) \bmod m \\ - (a-b) \bmod m = (a \bmod m - b \bmod m) \bmod m \\ - (a \cdot b) \pmod{m} = ((a \bmod m) \cdot (b \bmod m)) \bmod m \\ - a^b \bmod {m} = (a \bmod m)^b \bmod m -\end{gather*} -$$ -``` - -### Modular Exponentiation - -**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 ^ {\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$: -(not tested) - -```cpp -long long binpow(long long x, long long n, long long m) { - x %= m; - long long res = 1; - while (n > 0) { - if (n % 2 == 1) //if n is odd - res = res * x % m; - x = x * x % m; - n >>= 1; //divide by two - } - return res; -} -``` - -### Modular Inverse - -Under a prime moduli, division exists. - -## Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/5_Gold/MST.mdx b/content/5_Gold/MST.mdx deleted file mode 100644 index 958bc7f..0000000 --- a/content/5_Gold/MST.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: mst -title: "Minimum Spanning Trees" -author: Benjamin Qi -prerequisites: - - Gold - Shortest Paths - - Gold - Disjoint Set Union -description: A subset of the edges of a connected, undirected, edge-weighted graph that connects all the vertices to each other of minimum total weight, where no cycles are allowed. -frequency: 2 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - standard: [ - new Problem("Kattis", "MST", "minspantree", "Easy", false, ["MST"], ""), - new Problem("CSES", "Road Reparation", "1675", "Easy", false, ["MST"], ""), - ], - general: [ - new Problem("Old Silver", "SuperBull", "531", "Easy", false, ["MST"], ""), - new Problem("Gold", "Walk", "946", "Easy", false, ["Math","Prim"], ""), - new Problem("Gold", "Fencedin", "623", "Easy", false, ["MST"], ""), - new Problem("Plat", "Fencedin", "625", "Normal", false, ["Kruskal"], ""), - ], - } -}; - -## Sample - -<problems-list problems={metadata.problems.standard} /> - -## Tutorial - - - CPH 15 (Spanning Trees) - - [PAPS 12.4](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - Prim's Algorithm - - [cp-algo](https://cp-algorithms.com/graph/mst_prim.html) - - Similar to Dijkstra - - Kruskal's Algorithm - - [cp-algo 1](https://cp-algorithms.com/graph/mst_kruskal.html) - - [cp-algo 2](https://cp-algorithms.com/graph/mst_kruskal_with_dsu.html) - -## USACO Gold Problems - -<problems-list problems={metadata.problems.general} /> - -## Other Problems - - - [Birthday Gifts](https://www.hackerearth.com/practice/math/combinatorics/inclusion-exclusion/practice-problems/algorithm/mancunian-and-birthday-gifts-d44faa15/) [](73) - - [Spanning Tree Fraction](https://www.hackerrank.com/contests/w31/challenges/spanning-tree-fraction) [](78)
\ No newline at end of file diff --git a/content/5_Gold/Merging.mdx b/content/5_Gold/Merging.mdx deleted file mode 100644 index 76ac6ba..0000000 --- a/content/5_Gold/Merging.mdx +++ /dev/null @@ -1,114 +0,0 @@ ---- -id: merging -title: "Small-To-Large Merging" -author: Michael Cao -prerequisites: - - Silver - Depth First Search -description: "?" -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("CSES", "Distinct Colors", "1139", "Intro", false, ["Merging"]), - new Problem("CF", "Lomsat gelral", "contest/600/problem/E", "Normal", false, ["Merging"]), - new Problem("Plat", "Disruption", "842", "Hard", false, ["Merging"]), - new Problem("Plat", "Promotion Counting", "696", "Hard", false, ["Merging"], "Merge indexed sets"), - ] - } -}; - -## Additional Reading - - - CPH 18.4 - Merging Data Structures - - CF Blogs - - [Arpa](https://codeforces.com/blog/entry/44351) - - [tuwuna](https://codeforces.com/blog/entry/67696) - -# Merging Sets - -Let's consider a tree, rooted at node $1$, where each node has a color (see [CSES Distinct Colors](https://cses.fi/problemset/task/1139)). - -For each node, let's store a set containing only that node, and we want to merge the sets in the nodes subtree together such that each node has a set consisting of all colors in the nodes subtree. Doing this allows us to solve a variety of problems, such as query the number of distinct colors in each subtree. Doing this naively, however, yields a runtime complexity of $O(N^2)$. - -However, with just a few lines of code, we can significantly speed this up. - -```cpp -if(a.size() < b.size()){ //for two sets a and b - swap(a,b); -} -``` -In other words, by merging the smaller set into the larger one, the runtime complexity becomes $O(N\log N).$ - -<details> -<summary> Proof </summary> - -When merging two sets, you move from the smaller set to the larger set. If the size of the smaller set is $X$, then the size of the resulting set is at least $2X$. Thus, an element that has been moved $Y$ times will be in a set of size $2^Y$, and since the maximum size of a set is $N$ (the root), each element will be moved at most $O(\log N$) times leading to a total complexity of $O(N\log N)$. -</details> - -Additionally, a set doesn't have to be an `std::set`. Many data structures can be merged, such as `std::map` or even adjacency lists. - -<details> -<summary> Full Code </summary> - -```cpp -#include <bits/stdc++.h> - -using namespace std; - -const int MX = 200005; - -vector<int> adj[MX]; set<int> col[MX]; long long ans[MX]; -void dfs(int v, int p){ - for(int e : adj[v]){ - if(e != p){ - dfs(e, v); - if(col[v].size() < col[e].size()){ - swap(col[v], col[e]); - } - for(int a : col[e]){ - col[v].insert(a); - } - col[e].clear(); - } - } - ans[v] = col[v].size(); -} -int main() { - ios::sync_with_stdio(false); - cin.tie(0); - int n; cin >> n; - for(int i = 0; i < n; i++){ - int x; cin >> x; - col[i].insert(x); - } - for(int i = 0; i < n - 1; i++){ - int u,v; cin >> u >> v; - u--; v--; - adj[u].push_back(v); adj[v].push_back(u); - } - dfs(0,-1); - for(int i = 0; i < n; i++){ - cout << ans[i] << " "; - } -} -``` - -</details> - -<info-block title="Challenge"> - -Prove that if you instead merge sets that have size equal to the depths of the subtrees, then small to large merging does $O(N)$ insert calls. - -(be specific about what this means?) - -</info-block> - -## Problems - -(note about indexed set swap) - -<problems-list problems={metadata.problems.general} /> diff --git a/content/5_Gold/PURQ.mdx b/content/5_Gold/PURQ.mdx deleted file mode 100644 index 974f18a..0000000 --- a/content/5_Gold/PURQ.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -id: PURQ -title: "Point Update Range Query" -author: Benjamin Qi -prerequisites: - - Gold - Point Update Range Sum - - Gold - Static Range Queries - - Gold - Max Suffix Query with Insertions Only -description: Range queries for any associative operation over an array with updates, using segment tree. -frequency: 3 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Range Minimum Queries II", "1649", "Easy", false, ["PURQ"], ""), - ], - general: [ - new Problem("YS", "Point Set Range Composite", "point_set_range_composite", "Easy", false, ["PURQ"], ""), - new Problem("Gold", "Springboards", "995", "Normal", false, ["PURQ"], ""), - new Problem("Plat", "Nocross", "721", "Normal", false, ["PURQ"], ""), - new Problem("CSES", "Hotel Queries", "1143", "Normal", false, ["PURQ"], ""), - new Problem("CSES", "Subarray Sum Queries", "1190", "Normal", false, ["PURQ"], ""), - ], - waveletSam: [ - new Problem("YS", "Range K-th Smallest", "range_kth_smallest", "Hard", false, ["Wavelet"], ""), - ], - wavelet: [ - new Problem("Kattis", "Easy Query", "easyquery", "Hard", false, ["Wavelet"], ""), - new Problem("DMOJ", "Ninjaclasher's Wrath 2", "globexcup19s4", "Hard", false, ["Wavelet"], ""), - ], - } -}; - - - Normal SegTree - - [USACO Old Gold Seating](http://www.usaco.org/index.php?page=viewproblem2&cpid=231) (check ...) - - [USACO Old Gold Optimal Milking](http://www.usaco.org/index.php?page=viewproblem2&cpid=365) (check ...) - - [USACO Old Gold Marathon](http://www.usaco.org/index.php?page=viewproblem2&cpid=495) (check ...) - - [USACO Plat Balancing](http://www.usaco.org/index.php?page=viewproblem2&cpid=624) (check ...) - - [POI Cards](https://szkopul.edu.pl/problemset/problem/qpsk3ygf8MU7D_1Es0oc_xd8/site/?key=statement) [](81) - -<problems-list problems={metadata.problems.sample} /> - -A **segment tree** allows you to do point update and range query in $O(\log N)$ time each for any associative operation. Historically, no gold problem has required the use of a segment tree in place of a binary indexed tree, but it's still good to know (and you might find it simpler). - -### Tutorials - - - CPH 9.3 (Segment Trees) - - [PAPS 11.2.3](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [Codeforces Tutorial](http://codeforces.com/blog/entry/18051) - - [CSAcademy Tutorial](https://csacademy.com/lesson/segment_trees/) - - [cp-algorithms](https://cp-algorithms.com/data_structures/segment_tree.html) - - [Slides from CPC.3](https://github.com/SuprDewd/T-414-AFLV/tree/master/03_data_structures) - -### Problems - -Can try solving some of the BIT questions w/ segtree. - -<!-- <problems-list problems={metadata.problems.general} /> --> - -## Wavelet Tree - -<problems-list problems={metadata.problems.waveletSam} /> - -### Tutorial - - - [CF](http://codeforces.com/blog/entry/52854) - -<problems-list problems={metadata.problems.wavelet} />
\ No newline at end of file diff --git a/content/5_Gold/PURS.mdx b/content/5_Gold/PURS.mdx deleted file mode 100644 index 933a5ca..0000000 --- a/content/5_Gold/PURS.mdx +++ /dev/null @@ -1,112 +0,0 @@ ---- -id: PURS -title: "Point Update Range Sum" -author: Benjamin Qi -prerequisites: - - Silver - Prefix Sums -description: Introducing Binary Indexed Trees & Indexed Sets (C++ only). -frequency: 3 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - 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."), - new Problem("SPOJ", "Inversion Counting", "INVCNT", "Easy", false, ["PURS"]), - ], - practice: [ - new Problem("CSES", "List Removals", "1749", "Easy", false, undefined, "easy with indexed set"), - new Problem("CSES", "Salary Queries", "1144", "Easy", false, undefined, "easy with indexed set"), - new Problem("CSES", "Range Update Queries", "1651", "Easy", false, undefined, "not harder than point update range query"), - 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("CSES", "Distinct Values Queries", "1734", "Normal", false, undefined, "Do queries in increasing order of $a$."), - new Problem("CSES", "Robot Path", "1742", "Hard", false, undefined, ""), - new Problem("Gold", "Haircut", "1041", "Easy", false, undefined, ""), - new Problem("Gold", "Balanced Photo", "693", "Easy", false, undefined, ""), - new Problem("Gold", "Circle Cross", "719", "Easy", false, undefined, ""), - new Problem("Gold", "Sleepy Cow Sort", "898", "Easy", false, undefined, "All USACO problems (aside from some special exceptions) have only one possible output."), - new Problem("Plat", "Mincross", "720", "Easy", false, undefined, ""), - new Problem("Silver", "Out of Sorts", "834", "Normal", false, undefined, "aka Sorting Steps: https://csacademy.com/contest/round-42/task/sorting-steps/ - Of course, this doesn't require anything other than sorting but fast range sum queries may make this easier."), - new Problem("Gold", "Out of Sorts", "837", "Hard", false, undefined, ""), - ] - } -}; - -## Sample Problems - -<problems-list problems={metadata.problems.sample} /> - -## Binary Indexed Tree - -A *Binary Indexed Tree* allows you to perform the 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. - -Aka *Fenwick Tree*. - -### Tutorials - - * CPH 9.2, 9.4 - * very good - * [CSAcademy BIT](https://csacademy.com/lesson/fenwick_trees) - * also very good - * [cp-algorithms Fenwick Tree](https://cp-algorithms.com/data_structures/fenwick.html) - * extends to range increment and range query, although this is not necessary for gold - * [Topcoder BIT](https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/) - -My implementation can be found [here](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BIT%20(9.2).h), and can compute range sum queries for any number of dimensions. - -## Finding K-th Element in BIT? - -## Indexed Set - -In the special case where all elements of the array are either zero or one (which is the case for several gold problems), users of C++ will find [indexed set](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/STL%20(5)/IndexedSet.h) useful. Using this, we can solve "Inversion Counting" in just a few lines (with template). `Tree<int>` behaves mostly the same way as `set<int>` with the additional functions - - * `order_of_key(x)`: counts the number of elements in the indexed 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). - -See the link for more examples of usage. - -<spoiler title="INVCNT with Indexed Set"> - -```cpp -#include <bits/stdc++.h> -using namespace std; - -#include <ext/pb_ds/tree_policy.hpp> -#include <ext/pb_ds/assoc_container.hpp> -using namespace __gnu_pbds; -template <class T> using Tree = tree<T, null_type, less<T>, - rb_tree_tag, tree_order_statistics_node_update>; - -int main() { - int T; cin >> T; - for (int i = 0; i < T; ++i) { - int n; cin >> n; - Tree<int> TS; long long numInv = 0; - for (int j = 0; j < n; ++j) { - int x; cin >> x; - numInv += j-TS.order_of_key(x); // gives # elements before it > x - TS.insert(x); - } - cout << numInv << "\n"; - } -} -``` - -</spoiler> - -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 array position. - -## Practice Problems - - - Haircut, Balanced Photo, and Circle Cross are just variations on inversion counting. - -<problems-list problems={metadata.problems.practice} /> diff --git a/content/5_Gold/SP.mdx b/content/5_Gold/SP.mdx deleted file mode 100644 index 5cf7960..0000000 --- a/content/5_Gold/SP.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -id: sp -title: "Shortest Paths with Non-Negative Edge Weights" -author: Benjamin Qi -prerequisites: - - Gold - Breadth First Search -description: Introduces Dijkstra's Algorithm for a single source as well as Floyd-Warshall for All-Pairs Shortest Path. -frequency: 3 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Shortest Routes I", "1671", "Easy", false, ["SP"], "equivalent to [Kattis SSSP Non-Negative](https://open.kattis.com/problems/shortestpath1)"), - ], - dijk: [ - new Problem("CSES", "Flight Discount", "1195", "Easy", false, ["SP"], "one edge modified"), - 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", "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"], ""), - ], - apspSam: [ - new Problem("CSES", "Shortest Routes II", "1672", "Easy", false, ["APSP"], ""), - ], - apsp: [ - new Problem("Gold", "Moortal Combat", "971", "Hard", false, ["APSP", "DP"], ""), - ], - } -}; - -## Non-Negative Edge Weights - -<problems-list problems={metadata.problems.sample} /> - -Use *Dijkstra's Algorithm*. - -### Tutorial - - - CSES 13.2 - - [PAPS 12.3.1](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [cp-algo Dijkstra (Dense Graphs)](https://cp-algorithms.com/graph/dijkstra_sparse.html) - - [cp-algo Dijkstra (Sparse Graphs)](https://cp-algorithms.com/graph/dijkstra_sparse.html) - - Usually, it's this one that's applicable. - - [CPC.8](https://github.com/SuprDewd/T-414-AFLV/tree/master/08_graphs_2) - -### $O(M\log N)$ implementation - -The USACO training pages present a $O(N^2)$ version, although this is rarely used nowadays. - -<optional-content title="Faster Dijkstra"> - -Can be done in $O(M+N\log N)$ with Fibonacci heap. - -(link?) - -</optional-content> - -### Problems - -<problems-list problems={metadata.problems.dijk} /> - -## All Pairs Shortest Path (APSP) - -<problems-list problems={metadata.problems.apspSam} /> - -Use the *Floyd-Warshall* algorithm. - -### Standard - - - [CSES Shortest Routes II](https://cses.fi/problemset/task/1672) - -### Tutorial - - - CPH 13.3 - - [PAPS 12.3.3](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [cp-algo Floyd-Warshall](https://cp-algorithms.com/graph/all-pair-shortest-path-floyd-warshall.html) - -### Problems - -<problems-list problems={metadata.problems.apsp} /> - - - [USACO Moortal Cowmbat](http://usaco.org/index.php?page=viewproblem2&cpid=971) - - Use APSP before running DP.
\ No newline at end of file diff --git a/content/5_Gold/TopoSort.mdx b/content/5_Gold/TopoSort.mdx deleted file mode 100644 index 2997d98..0000000 --- a/content/5_Gold/TopoSort.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -id: toposort -title: "Topological Sort" -author: Benjamin Qi, Michael Cao -prerequisites: - - Gold - Breadth First Search - - Gold - Introduction to Dynamic Programming -description: "?" -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Course Schedule", "1679", "Easy", false, []), - ], - general: [ - new Problem("CSES", "Game Routes", "1681", "Easy", false, [], "counting paths on DAG"), - new Problem("Kattis", "Quantum", "https://open.kattis.com/contests/acpc17open/problems/quantumsuperposition", "Easy", false, [], "enumerating paths on DAG"), - new Problem("Gold", "Timeline", "1017", "Easy", false, [], "not explicitly given, but graph is a DAG"), - new Problem("Gold", "Milking Order", "838", "Normal", false, ["TopoSort", "Binary Search"]), - new Problem("CSES", "Course Schedule II", "1681", "Hard", false, [], "equivalent to [Minimal Labels](https://codeforces.com/contest/825/problem/E)"), - ], - } -}; - -To review, a **directed** graph consists of edges that can only be traversed in one direction. Additionally, a **acyclic** graph defines a graph which does not contain cycles, meaning you are unable to traverse across one or more edges and return to the node you started on. Putting these definitions together, a **directed acyclic** graph, sometimes abbreviated as DAG, is a graph which has edges which can only be traversed in one direction and does not contain cycles. - -## Topological Sort - -<problems-list problems={metadata.problems.sample} /> - -A [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) of a directed acyclic graph is a linear ordering of its vertices such that for every directed edge $u\to v$ from vertex $u$ to vertex $v$, $u$ comes before $v$ in the ordering. - -### Tutorial - - - CPH 16.1, 16.2 - - DFS - - [cp-algorithms](https://cp-algorithms.com/graph/topological-sort.html) - - DFS - - [CSAcademy](https://csacademy.com/lesson/topological_sorting) - - both BFS, DFS - -The BFS version, known as [Kahn's Algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm), makes it obvious how to extract the lexicographically minimum topological sort. - -## Dynamic Programming - - - [PAPS 9.1](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - -One useful property of directed acyclic graphs is, as the name suggests, that there exists no cycles. If we consider each node in the graph as a state, we can perform dynamic programming on the graph if we process the states in an order that guarantees for every edge, $u\to v$ that $u$ is processed before $v$. Fortunately, this is the exact definition of a topological sort! - -Let's consider the classical problem [CSES Longest Flight Route](https://cses.fi/problemset/task/1680), where we must find the longest path in a DAG. - -<spoiler title="Solution"> - -Let $dp[v]$ denote the length of the longest path ending at the node $v$. Clearly - -$$ -dp[v]=\max_{\text{edge } u\to v \text{ exists}}dp[u]+1, -$$ - -or zero if $v$ has in-degree $0$. If we process the states in topological order, it is guarangeed that $dp[u]$ will already have been computed before computing $dp[v]$. - -</spoiler> - -<!-- However, not all problems clearly give you directed acyclic graphs (ex. [Plat - Cave Paintings](http://usaco.org/index.php?page=viewproblem2&cpid=996)). An important step in many problems is to reduce the statement into a directed acyclic graph. See the editorial of the linked problem for more information. - -(Ben - this last paragraph doesn't seem very helpful.) --> - -## Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/5_Gold/Tree_Euler.mdx b/content/5_Gold/Tree_Euler.mdx deleted file mode 100644 index d1db4fc..0000000 --- a/content/5_Gold/Tree_Euler.mdx +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: tree-euler -title: "Euler Tour on Tree" -author: "?" -prerequisites: - - Silver - Depth First Search - - Gold - Static Range Queries - - Gold - Point Update Range Sum -description: Subtree updates and queries and another way to compute lowest common ancestors. -frequency: 2 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("CSES", "Subtree Queries", "1137", "Easy", false, ["Euler-Tree"], "equivalent to https://judge.yosupo.jp/problem/vertex_add_subtree_sum"), - new Problem("CSES", "Company Queries II", "1688", "Easy", false, ["LCA"], ""), - ], - problems: [ - new Problem("CSES", "Path Queries", "1138", "Easy", false, ["Euler-Tree","PURS"], "equivalent to https://judge.yosupo.jp/problem/vertex_add_path_sum"), - new Problem("Gold", "Cow Land", "921", "Normal", false, ["Euler-Tree","PURS"], ""), - new Problem("Gold", "Milk Visits", "970", "Normal", false, ["Euler-Tree"], ""), - new Problem("Plat", "Promotion Counting", "696", "Normal", false, ["Euler-Tree","PURS"], ""), - new Problem("Plat", "Snow-Cow", "973", "Hard", false, ["Euler-Tree","PURS"], ""), - ] - } -}; - -## Sample Problems - -<problems-list problems={metadata.problems.sample} /> - -## Tutorial - - - CPH 18.2 - - [cp-algorithms - LCA with Sparse Table](https://cp-algorithms.com/graph/lca.html) - -## Problems - -<problems-list problems={metadata.problems.problems} />
\ No newline at end of file diff --git a/content/5_Plat/2DRQ.mdx b/content/5_Plat/2DRQ.mdx new file mode 100644 index 0000000..1e5b088 --- /dev/null +++ b/content/5_Plat/2DRQ.mdx @@ -0,0 +1,125 @@ +--- +id: 2DRQ +title: "2D Range Queries" +author: Benjamin Qi +prerequisites: + - RURQ +description: "Extending Range Queries to 2D (and beyond)." +frequency: 1 +--- + +export const problems = { + bitSam: [ + new Problem("CSES", "Forest Queries II", "1739", "Easy", false, ["2D BIT"], "[thecodingwizard's implementation](https://github.com/thecodingwizard/competitive-programming/blob/master/cses/Forest%20Queries%20II.cpp)"), + ], + bit: [ + new Problem("DMOJ", "Crowded Cities", "bfs17p6", "Normal", false, ["2D Max BIT"], ""), + new Problem("IOI", "Pairs", "https://wcipeg.com/problem/ioi0722", "Normal", false, ["3D BIT"], ""), + ], + offSam: [ + new Problem("DMOJ", "Soriya's Programming Project", "dmopc19c7p5", "Normal", false, ["2D BIT"], ""), + ], + off: [ + new Problem("Plat", "Friendcross", "722", "Hard", false, ["2D BIT"], ""), + new Problem("Plat", "Mowing the Field", "601", "Hard", false, ["2D BIT"], ""), + ], + seg: [ + new Problem("POI", "Tetris 3D", "https://szkopul.edu.pl/problemset/problem/OQjANSOOD_-c38gh8p6g3Gxp/site/?key=statement", "Hard", false, ["2D Seg"], ""), + new Problem("ojuz", "IOI 2013 - Game", "IOI13_game", "Very Hard", false, ["2D Seg"], "Alternatively, use BBST in place of sparse segment tree (see Advanced - Treaps)"), + new Problem("ojuz", "JOI - Golf", "JOI17_golf", "Very Hard", false, ["Seg"], ""), + ], +}; + +See [my implementations](https://github.com/bqi343/USACO/tree/master/Implementations/content/data-structures/2D%20Range%20Queries%20(15.2)). + +## 2D RMQ + +<Resources> + <Resource source="CF" title="retrograd - Multi-Dimensional RMQ" url="blog/entry/53810" starred></Resource> +</Resources> + + - GP of Serbia 2020 B + +## 2D BIT + +<Problems problems={problems.bitSam} /> + +### Tutorial + +<Resources> + <Resource source="GFG" title="2D BIT" url="two-dimensional-binary-indexed-tree-or-fenwick-tree"></Resource> + <Resource source="TC" title="Binary Indexed Trees" url="binary-indexed-trees"></Resource> +</Resources> + +### Problems + +<Problems problems={problems.bit} /> + +<Optional title="Range Update and Range Query in Higher Dimensions"> + +Lazy propagation on segment trees does not extend to higher dimensions. However, you can extend the 1D BIT solution to solve range increment range sum in higher dimensions as well! See [this paper](https://arxiv.org/pdf/1311.6093.pdf) for details. + + - USACO Camp - "Cows Play Global Thermonuclear War" (2D case) + +</Optional> + +## 2D Offline Sum Queries + +<Problems problems={problems.offSam} /> + +The intended complexity is $O(N\log^2 N)$ with a good constant factor. This requires updating points and querying rectangle sums $N$ times for points with coordinates in the range $[1,N]$. However The 2D BITs mentioned above use $O(N^2)$ memory, which is too much. Since we know all of the updates and queries beforehand, we can reduce the memory usage while maintaining a decent constant factor. + +### Idea 1: Use an unordered map instead of a 2D array. + +Bad idea ... This gives $O(N\log^2N)$ memory and time and the constant factors for both are terrible. + +### Idea 2: Compress the points to be updated so that you only need $O(N\log N)$ memory. + +This doesn't require knowing the queries beforehand. + + - [my 1D offline BIT](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BIToff.h) + - [my 2D offline BIT](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/2D%20Range%20Queries%20(15.2)/BIT2DOff%20(15.2).h) + +It's a bit difficult to pass the above problem within the time limit. Make sure to use fast input (and not `endl`)! + + - [thecodingwizard's implementation with 2D offline BIT above](https://github.com/thecodingwizard/competitive-programming/blob/master/DMOJ/Soriyas%20Programming%20Project.cpp) + +### Idea 3: Use divide & conquer with a 1D BIT + + - mentioned in [this article](https://robert1003.github.io/2020/01/31/cdq-divide-and-conquer.html) + - [thecodingwizard's (messy) implementation](https://github.com/thecodingwizard/competitive-programming/blob/master/DMOJ/Soriya%20Programming%20Project%201d%20BIT%20cdq%20dnc.cpp) based off above + +### Problems + +<Problems problems={problems.off} /> + +## 2D Segment Tree + +Basically a segment tree of (maybe sparse) segment trees (or BBSTs, see "Advanced - Treap"). + +<Info title="Pro Tip"> + +This is **not** the same as [Quadtree](https://en.wikipedia.org/wiki/Quadtree). If the coordinates go up to $C$, then 2D segment tree queries run in $O(\log^2C)$ time each but some queries make Quadtree take $\Theta(C)$ time! + +</Info> + +### Short Description + +<Resources> + <Resource source="CPH" title="28.2 (Sparse SegTree), 28.4 (2D)">brief description</Resource> + <Resource source="USACO" title="Analysis - Mowing the Field" url="http://www.usaco.org/current/data/sol_mowing_platinum_jan16.html">code</Resource> +</Resources> + +### Reducing Memory Usage + +Naively, inserting $N$ elements into a sparse segment tree requires $O(N\log C)$, giving a memory bound of $O(N\log^2C)$ on 2D segment tree memory. This is usually too much for $N=10^5$ (although it sufficed for "Mowing the Field" due to the higher memory limit). + +To resolve this, reduce the memory usage of sparse segment tree while maintaing the same $O(N\log C)$ insertion time (see the solution for IOI Game below for details). + +(implementation) + +### Problems + +Can also try the USACO problems from above. + +<Problems problems={problems.seg} />
\ No newline at end of file diff --git a/content/5_Plat/BCC_2CC.mdx b/content/5_Plat/BCC_2CC.mdx new file mode 100644 index 0000000..865ebaf --- /dev/null +++ b/content/5_Plat/BCC_2CC.mdx @@ -0,0 +1,125 @@ +--- +id: BCC-2CC +title: "BCCs and 2CCs" +author: Benjamin Qi +prerequisites: + - dfs + - bin-jump +description: "Precomputing parents of nodes in powers of two to find the xth parent of a node efficently." +frequency: 1 +--- +import { Problem } from "../models"; + +export const problems = { + sam2: [ + new Problem("YS", "Two-Edge-Connected Components", "two_edge_connected_components", "Easy", false, [], ""), + ], + disrupt: [ + new Problem("Plat", "Disruption", "842", "Normal", false, ["Merging"]), + ], + probs2: [ + new Problem("CSA", "One-Way Streets", "one-way-streets", "Easy", false, [], ""), + ], + bccSam: [ + new Problem("CSES", "Forbidden Cities", "1705", "Normal", false, [], ""), + ], + gen: [ + new Problem("POI", "Blockade", "https://szkopul.edu.pl/problemset/problem/eDt8w290owtatmCjad0O0ywk/site/?key=statement", "Normal", false, [], ""), + new Problem("ojuz", "APIO - Duathlon", "APIO18_duathlon", "Normal", false, [], ""), + new Problem("Plat", "Push a Box", "769", "Very Hard", false, [], ""), + new Problem("DMOJ", "Investment", "tle17c1p6", "Hard", false, [], ""), + new Problem("ojuz", "CEOI - Pipes", "CEOI15_pipes", "Hard", false, [], ""), + ], +}; + +<Resources> + <Resource source="CF" title="DFS Tree + Bridges" url="blog/entry/68138"> </Resource> +</Resources> + +## 2-Edge-Connected Components + +<Problems problems={problems.sam2} /> + +(implementation) + +### With DSU + +<Problems problems={problems.disrupt} /> + +The analysis for the above problem mentions an $O(m\alpha(n))$ solution. Although this is not a two-connected component problem, we can in fact use DSU to generate two-connected components. + +(explanation?) + +<Spoiler title="Code"> + +The DSU operations take $O(\log n)$ rather than $O(\alpha(n))$ because the DSU does not use union by size, but it's easy to change this. + +```cpp +struct TwoEdgeCC { + struct { + vi e; void init(int n) { e = vi(n,-1); } + int get(int x) { return e[x] < 0 ? x : e[x] = get(e[x]); } + bool unite(int x, int y) { // set par[y] = x + x = get(x), y = get(y); if (x == y) return 0; + e[x] += e[y]; e[y] = x; return 1; + } + } DSU; + int N; vector<vi> adj; vi depth, par; + vpi extra; + void init(int _N) { + N = _N; DSU.init(N); + adj.rsz(N), depth.rsz(N), par = vi(N,-1); + } + void dfs(int x) { + trav(t,adj[x]) if (t != par[x]) + par[t] = x, depth[t] = depth[x]+1, dfs(t); + } + void ae(int a, int b) { + if (DSU.unite(a,b)) adj[a].pb(b), adj[b].pb(a); // edge of forest + else extra.pb({a,b}); // extra edge + } + void ad(int a, int b) { + while (1) { + a = DSU.get(a), b = DSU.get(b); + if (a == b) return; + if (depth[a] < depth[b]) swap(a,b); + assert(par[a] != -1 && DSU.unite(par[a],a)); + } + } + void gen() { + F0R(i,N) if (par[i] == -1) dfs(i); // independently for each connected component + DSU.init(N); trav(t,extra) ad(t.f,t.s); // add non-spanning edges +}; +``` + +</Spoiler> + +### Problems + +<Problems problems={problems.probs2} /> + + - SRM 787 1000 + +## [Biconnected Components](https://en.wikipedia.org/wiki/Biconnected_component) + +<Problems problems={problems.bccSam} /> + +note that BCCs contain EDGES not VERTICES + +Related topics include + + - Articulation Points + - Bridges + - Block-Cut Tree + +### Tutorial + +<Resources> + <Resource source="GFG" title="Articulation Points (aka Cut Vertices)" url="articulation-points-or-cut-vertices-in-a-graph">maybe not completely correct</Resource> +</Resources> + +(implementation) + +### Problems + +<Problems problems={problems.gen} />
\ No newline at end of file diff --git a/content/6_Plat/Bin_Jump.mdx b/content/5_Plat/Bin_Jump.mdx index 569daf8..884db3d 100644 --- a/content/6_Plat/Bin_Jump.mdx +++ b/content/5_Plat/Bin_Jump.mdx @@ -2,55 +2,59 @@ id: bin-jump title: "Binary Jumping" author: Benjamin Qi -description: Introduces the problems of finding level ancestors in a tree and computing the lowest common ancestors. +description: "Introduces the problems of finding level ancestors in a tree and computing the lowest common ancestors." frequency: 3 --- import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { sample: [ - new Problem("CSES", "Planet Queries I", "1750", "Easy", false, ["Binary Jumping"], ""), new Problem("CSES", "Company Queries I", "1687", "Easy", false, ["Binary Jumping"], ""), + ], + lca: [ new Problem("CSES", "Company Queries II", "1688", "Easy", false, ["LCA"], ""), ], general: [ + new Problem("CSES", "Planets Queries I", "1750", "Easy", false, ["Binary Jumping"], ""), new Problem("CSES", "Distance Queries", "1135", "Easy", false, ["LCA"], ""), new Problem("Plat", "Max Flow", "576", "Easy", false, ["LCA"], ""), new Problem("CSA", "Root LCA Queries", "root-lca-queries", "Normal", false, ["LCA"], ""), new Problem("CSES", "Planets Queries II", "1160", "Normal", false, ["LCA"], ""), new Problem("Plat", "Disruption", "842", "Normal", false, ["LCA"], ""), new Problem("DMOJ", "Hot & Cold", "bts17p7", "Normal", false, ["LCA"], ""), + new Problem("TOKI", "Functional Constraint", "https://tlx.toki.id/contests/troc-12-div-1/problems/D", "Hard", false, ["LCA"], ""), + new Problem("TOKI", "Graph & Destination", "https://tlx.toki.id/contests/troc-13-div-1/problems/E", "Hard", false, ["LCA"], ""), new Problem("Plat", "Tree Boxes", "948", "Hard", false, ["LCA"], "interactive!!"), new Problem("Plat", "Newbarns", "817", "Hard", false, ["Diameter"], "Copy of CF Brain Network \"Hard\": https://codeforces.com/contest/690/problem/C3"), new Problem("Plat", "Gathering", "866", "Hard", false, ["LCA"], "interactive!!"), new Problem("Plat", "Exercise", "901", "Very Hard", false, ["LCA"], ""), ] - } }; ## Binary Jumping -## Sample Problems - -<problems-list problems={metadata.problems.sample} /> +<Problems problems={problems.sample} /> ## Lowest Common Ancestor +<Problems problems={problems.lca} /> + ### Tutorial - - CPH 18.1, 18.3 - - [cp-algorithms](https://cp-algorithms.com/) +<Resources> + <Resource source="CPH" title="18.1, 18.3 (Method 1) - Finding Ancestors" starred></Resource> + <Resource source="cp-algo" title="LCA with Binary Lifting" url="graph/lca_binary_lifting.html"> </Resource> +</Resources> -<optional-content title="Improvements"> +<Optional title="Improvements"> - [CF: $O(\log N)$ queries and $O(N)$ memory](https://codeforces.com/blog/entry/74847) - [Wikipedia: $O(1)$ queries and $O(N)$ preprocessing time](https://en.wikipedia.org/wiki/Level_ancestor_problem#Ladder_algorithm) - though explanation is not the greatest -</optional-content> +</Optional> ### Problems -<problems-list problems={metadata.problems.general} />
\ No newline at end of file +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/6_Plat/Bitsets.mdx b/content/5_Plat/Bitsets.mdx index 3059f47..d478af6 100644 --- a/content/6_Plat/Bitsets.mdx +++ b/content/5_Plat/Bitsets.mdx @@ -2,15 +2,15 @@ id: bitsets title: "Bitsets" author: Benjamin Qi -description: Several examples of how bitsets give some unintended solutions on recent USACO problems. +description: "Several examples of how bitsets give some unintended solutions on recent USACO problems." prerequisites: - Errichto - Bitwise Operations Pt 1 +frequency: 2 --- import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { school: [ new Problem("CSES", "School Excursion", "1706", "Easy", false, ["Knapsack", "Bitset"], ""), ], @@ -25,25 +25,26 @@ export const metadata = { ], ad: [ new Problem("Plat", "Equilateral Triangles", "1021", "Normal", false, ["Bitset, Sliding Window"], "Again, the intended solution runs in $O(N^3)$. Of course, it is still possible to pass $O(N^4)$ solutions with bitset! See the analysis [here](http://www.usaco.org/current/data/sol_triangles_platinum_feb20.html)."), - new Problem("CSES", "BOI Nautilus", "https://cses.fi/247/submit/B", "Normal", false, ["Bitset"], ""), + new Problem("CSES", "BOI - Nautilus", "https://cses.fi/247/submit/B", "Normal", false, ["Bitset"], ""), + new Problem("ojuz", "IZhO - Bootfall", "IZhO17_bootfall", "Hard", false, ["Knapsack", "Bitset"], ""), ], - } }; ## Tutorial -tl;dr some operations are 32x-64x faster compared to a boolean array. +tl;dr some operations are 32x-64x faster compared to a boolean array. See the [C++ Reference](http://www.cplusplus.com/reference/bitset/bitset/) for the operations you can perform. - - [C++ Reference](http://www.cplusplus.com/reference/bitset/bitset/) - - [Errichto - Bitwise Operations Pt 2](https://codeforces.com/blog/entry/73558) +<Resources> + <Resource source="CF" title="Errichto - Bitwise Operations Pt 2" url="blog/entry/73558"></Resource> +</Resources> ## Knapsack -<problems-list problems={metadata.problems.school} /> +<Problems problems={problems.school} /> Of course, the first step is to generate the sizes of each connected component. -<spoiler title="Input"> +<Spoiler title="Input"> ```cpp #include <bits/stdc++.h> @@ -76,13 +77,13 @@ void init() { } ``` -</spoiler> +</Spoiler> A naive knapsack solution would be as follows. For each $0\le i\le \texttt{comps.size()}$, let $\texttt{dp}[i][j]=1$ if there exists a subset of the first $i$ components whose sizes sum to $j$. Then the answer will be stored in $\texttt{dp}[i]$. This runs in $O(N^2)$ and is too slow if implemented naively, but we can use bitset to speed it up! Note: you can't store all $N$ bitsets in memory at the same time (more on that below). -<spoiler title="Full Solution"> +<Spoiler title="Full Solution"> ```cpp int main() { @@ -94,13 +95,13 @@ int main() { } ``` -</spoiler> +</Spoiler> **Challenge**: This solution runs in $\approx 0.3\text{s}$ when $N=10^5$ and there are no edges. Find a faster solution which can also be sped up with bitset (my solution runs in 0.03s). ## Cowpatibility (Gold) -<problems-list problems={metadata.problems.cow} /> +<Problems problems={problems.cow} /> Label the cows from $0\ldots N-1$. For two cows $x$ and $y$ set `adj[x][y]=1` if they share a common flavor. Then the number of pairs of cows that are compatible (counting each pair where $x$ and $y$ are distinct twice) is equal to the sum of `adj[x].count()` over all $x$. It remains to compute `adj[x]` for all $x$. @@ -108,7 +109,7 @@ Unfortunately, storing $N$ bitsets each with $N$ bits takes up $\frac{50000^2}{3 First, we read in all of the flavors. -<spoiler title="Input"> +<Spoiler title="Input"> ```cpp #include <bits/stdc++.h> @@ -136,7 +137,7 @@ void input() { } ``` -</spoiler> +</Spoiler> Then for each flavor, we can look at all pairs of cows that share that flavor and update the adjacency lists for those $x\in [0,HALF)$. @@ -160,7 +161,7 @@ for (int i = 1; i <= 1000000; ++i) if (flav[i].size() > 0) { The full main function is as follows: -<spoiler title="Full Solution"> +<Spoiler title="Full Solution"> ```cpp int main() { @@ -180,17 +181,17 @@ int main() { } ``` -</spoiler> +</Spoiler> Apparently no test case contains more than $25000$ distinct colors, so we don't actually need to split the calculation into two halves. ## Lots of Triangles -<problems-list problems={metadata.problems.lots} /> +<Problems problems={problems.lots} /> First, we read in the input data. `cross(a,b,c)` is positive iff `c` lies to the left of the line from `a` to `b`. -<spoiler title="Input"> +<Spoiler title="Input"> ```cpp #include <bits/stdc++.h> @@ -220,11 +221,11 @@ void input() { } ``` -</spoiler> +</Spoiler> There are $O(N^3)$ possible lots. Trying all possible lots and counting the number of trees that lie within each in $O(N)$ for a total time complexity of $O(N^4)$ should solve somewhere between 2 and 5 test cases. Given a triangle `t[0], t[1], t[2]` with positive area, tree `x` lies within it iff `x` is to the left of each of sides `(t[0],t[1])`,` (t[1],t[2])`, and `(t[2],t[0])`. -<spoiler title="Slow Solution"> +<Spoiler title="Slow Solution"> ```cpp int main() { @@ -248,11 +249,11 @@ int main() { } ``` -</spoiler> +</Spoiler> The analysis describes how to count the number of trees within a lot in $O(1)$, which is sufficient to solve the problem. However, $O(N)$ is actually sufficient as long as we divide by the bitset constant. Let `b[i][j][k]=1` if `k` lies to the left of side `(i,j)`. Then `x` lies within triangle `(t[0],t[1],t[2])` as long as `b[t[0]][t[1]][x]=b[t[1]][t[2]][x]=b[t[2]][t[0]][x]=1`. We can count the number of `x` such that this holds true by taking the bitwise AND of the bitsets for all three sides and then counting the number of bits in the result. -<spoiler title="Fast Solution"> +<Spoiler title="Fast Solution"> ```cpp bitset<300> b[300][300]; @@ -275,7 +276,7 @@ int main() { for (int i = 0; i < N-2; ++i) cout << res[i] << "\n"; } ``` -</spoiler> +</Spoiler> ## Knapsack Again (GP of Bytedance 2020 F) @@ -293,7 +294,7 @@ Since we only need to keep track of $2X+1$ values for each $i$, this solution ru Intuitively, the random shuffle reduces the optimal subset to some random walk which should have variance at most $\max a_i\cdot \sqrt N$, so it suffices to take $X\approx \max a_i\cdot \sqrt N$. (Though I'm not completely convinced that this works, does anyone know how to bound the failure probability of this algorithm precisely?) -<spoiler title="Solution"> +<Spoiler title="Solution"> ```cpp #include <bits/stdc++.h> @@ -333,7 +334,7 @@ int main() { for (int i = 0; i < T; ++i) cout << solve() << "\n"; } ``` -</spoiler> +</Spoiler> ## Other Applications @@ -347,10 +348,10 @@ Operations such as `_Find_first()` and `_Find_next()` mentioned in Errichto's bl Regarding the last application: -<problems-list problems={metadata.problems.bfs} /> +<Problems problems={problems.bfs} /> In USACO Camp, this problem appeared with $N\le 10^5$ and a large time limit ... ## Additional Problems -<problems-list problems={metadata.problems.ad} /> +<Problems problems={problems.ad} /> diff --git a/content/5_Plat/Centroid.mdx b/content/5_Plat/Centroid.mdx new file mode 100644 index 0000000..255e44e --- /dev/null +++ b/content/5_Plat/Centroid.mdx @@ -0,0 +1,137 @@ +--- +id: centroid +title: "Centroid Decomposition" +author: Benjamin Qi, Siyong Huang +prerequisites: + - dfs + - SRQ +description: "?" +frequency: 1 +--- + +<!-- + +TODO: + - Add more resources + +--> + +import { Problem } from "../models"; + +export const problems = { + general: [ + new Problem("CF", "Ciel the Commander", "problemset/problem/321/C", "Easy", false, ["Centroid"], ""), + new Problem("Plat", "New Barns", "817", "Normal", true, ["Centroid"], ""), + new Problem("CF", "Sherlock's bet to Moriarty", "contest/776/problem/F", "Normal", false, ["Centroid"], ""), + new Problem("CF", "Digit Tree", "contest/715/problem/C", "Normal", false, ["Centroid", "NT"], ""), + new Problem("CF", "Double Tree", "contest/1140/problem/G", "Normal", false, ["Centroid", "DP"], ""), + new Problem("ojuz", "JOI - Factories", "JOI14_factories", "Normal", false, ["Centroid"], ""), + new Problem("CF", "Sum of Prefix Sums", "contest/1303/problem/G", "Hard", false, ["Centroid", "CHT"], ""), + new Problem("YS", "Frequency Table of Tree Distance", "frequency_table_of_tree_distance", "Hard", false, ["Centroid", "FFT"], ""), + new Problem("DMOJ", "Bob Equilibrium", "dmopc19c7p6", "Hard", false, ["Centroid"], "tight time limit"), + new Problem("DMOJ", "Time Traveller Imaxblue", "tc19summerh", "Hard", false, ["Centroid"], ""), + new Problem("ojuz", "JOI - Synchronization", "JOI13_synchronization", "Hard", false, ["Centroid", "Small to Large"], "Looks like $O(N \log^3 N)$ is very fast!"), + new Problem("Plat", "At Large", "793", "Very Hard", false, ["Centroid"], "tight time limit"), + ] +}; + +## Centroid Decomposition + +**Centroid Decomposition** is a divide and conquer technique for trees. +The **centroid** of a tree is a node which, if rooted, results in no other nodes having a subtree of size greater than $\frac{N}{2}$. +**Centroid Decomposition** works by repeated splitting the tree and each of the resulting subgraphs at the centroid, producing $O(\log N)$ layers of subgraphs. + +### Tutorial + +<Resources> + <Resource source="Carpanese" title="Illustrated Intro to Centroid Decomposition" url="https://medium.com/carpanese/an-illustrated-introduction-to-centroid-decomposition-8c1989d53308" starred></Resource> + <Resource source="GFG" title="Centroid Decomposition of Tree" url="centroid-decomposition-of-tree"></Resource> +</Resources> + +### Implementation + +<LanguageSection> + +<CPPSection> + +<!-- pulled from https://codeforces.com/contest/1303/submission/76216413, which I think is my most recent centroid problem --> + +```cpp +bool r[MN];//removed +int s[MN];//subtree size +int dfs(int n, int p = 0) +{ + s[n] = 1; + for(int x : a[n]) + if(x != p && !r[x]) + s[n] += dfs(x, n); + return s[n]; +} +int get_centroid(int n, int ms, int p = 0)//n = node, ms = size of tree, p = parent +{ + for(int x : a[n]) + if(x != p && !r[x]) + if(s[x]*2 > ms) + return get_centroid(x, ms, n); + return n; +} +void centroid(int n = 1) +{ + int C = get_centroid(n, dfs(n)); + + //do something + + r[C] = 1; + for(int x : a[C]) + if(!r[x]) + centroid(x); +} +``` + +</CPPSection> + +<JavaSection> + +<!-- Modified from above. I can't guarauntee it compiles and functions as expected --> + +```java +boolean[] r = new boolean[MN];//removed +int[] s = new int[MN];//subtree size +public int dfs(int n, int p) +{ + s[n] = 1; + for(int x : a[n]) + if(x != p && !r[x]) + s[n] += dfs(x, n); + return s[n]; +} +public int get_centroid(int n, int ms, int p)//n = node, ms = size of tree, p = parent +{ + for(int x : a[n]) + if(x != p && !r[x]) + if(s[x]*2 > ms) + return get_centroid(x, ms, n); + return n; +} +public void centroid(int n) +{ + int C = get_centroid(n, dfs(n, 0), 0); + + //do something + + r[C] = 1; + for(int x : a[C]) + if(!r[x]) + centroid(x); +} +``` + +</JavaSection> + +</LanguageSection> + +### Problems + +<Problems problems={problems.general} /> + +*Note:* Unfortunately, it seems like constant factor is especially important for DMOJ. :| diff --git a/content/5_Plat/Convex_Hull.mdx b/content/5_Plat/Convex_Hull.mdx new file mode 100644 index 0000000..4917c08 --- /dev/null +++ b/content/5_Plat/Convex_Hull.mdx @@ -0,0 +1,71 @@ +--- +id: convex-hull +title: "Convex Hull" +author: Benjamin Qi +description: Smallest convex polygon containing a set of points on a grid. +prerequisites: + - geo-pri +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("Kattis", "Convex Hull", "convexhull", "Easy", false, ["convex"], ""), + ], + sample2: [ + new Problem("Kattis","Robert Hood", "roberthood", "Easy", false, ["convex"], "doesn't require convex hull since coordinates are small"), + ], + general: [ + new Problem("Plat", "Balance Beam", "864", "Hard", false, ["convex"], ""), + new Problem("CF", "Water Balance", "problemset/problem/1299/C", "Easy", false, ["convex"], ""), + new Problem("CF", "Geometers Anonymous", "problemset/problem/1195/F", "Normal", false, ["convex", "PURS"], ""), + new Problem("Old Gold", "Cow Curling", "382", "Normal", false, ["convex"], "testing whether points are inside hull"), + new Problem("AC", "AGC E - Random Pawn", "https://atcoder.jp/contests/agc044/tasks/agc044_e", "Insane", false, ["convex"], ""), + ], + rotating: [ + new Problem("Kattis", "Fence Orthogonality", "fenceortho", "Hard", false, ["convex"], "enclosing rectangle"), + ], + cht: [ + new Problem("Plat", "Circular Barn", "626", "Hard", false, ["DP", "convex"], ""), + new Problem("Plat", "Falling Portals", "998", "Very Hard", false, ["convex"], ""), + ], +}; + +## [Convex Hull](https://en.wikipedia.org/wiki/Convex_hull_algorithms) + +<Problems problems={problems.sample} /> + +### Tutorial + + - Graham Scan + - [cp-algo](https://cp-algorithms.com/geometry/grahams-scan-convex-hull.html) + - [My Implementation](https://github.com/bqi343/USACO/blob/master/Implementations/content/geometry%20(13)/Polygons/ConvexHull2.h) + - Monotone Chain + - CPH 30.3 (brief) + - [Wikipedia](https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain) + - [My Implementation](https://github.com/bqi343/USACO/blob/master/Implementations/content/geometry%20(13)/Polygons/ConvexHull%20(13.2).h) + +<Problems problems={problems.general} /> + +## Rotating Caliphers + +<Problems problems={problems.sample2} /> + +<Resources> + <Resource source="CF" title="Rotating calipers technique and applications" url="blog/entry/46162"> </Resource> +</Resources> + +<Problems problems={problems.rotating} /> + +## Convex Hull Trick + +<Resources> + <Resource source="cp-algo" title="Convex Hull Trick" url="geometry/convex_hull_trick.html" starred> </Resource> + <Resource source="CF" title="Convex Hull Trick - Geo Being Useful" url="blog/entry/63823" starred> </Resource> +</Resources> + +<Problems problems={problems.cht} /> + +https://codeforces.com/contest/1083/problem/E
\ No newline at end of file diff --git a/content/5_Plat/DP_Bitmasks.mdx b/content/5_Plat/DP_Bitmasks.mdx new file mode 100644 index 0000000..b895ab9 --- /dev/null +++ b/content/5_Plat/DP_Bitmasks.mdx @@ -0,0 +1,61 @@ +--- +id: dp-bitmasks +title: "Dynamic Programming on Bitmasks" +author: Michael Cao +prerequisites: + - Bit Operations + - intro-dp +description: DP problems that require iterating over subsets. +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + general: [ + new Problem("AC", "Matching", "https://atcoder.jp/contests/dp/tasks/dp_o?lang=en", "Easy", false, ["Bitmasks"], ""), + new Problem("CSES", "Hamiltonian Flights", "1690", "Easy", false, ["Bitmasks"], ""), + new Problem("CF", "Square Subsets", "contest/895/problem/C", "Easy", false, [], ""), + new Problem("Old Gold", "Guard Mark", "494", "Easy", false, ["Bitmasks"], ""), + new Problem("Old Gold", "Moovie Mooving", "515", "Easy", false,[ "Bitmasks"], ""), + new Problem("Kattis", "Cat & Mice", "catandmice", "Normal", false, ["Bitmasks", "Geometry"], ""), + new Problem("CSES", "Elevator Rides", "1653", "Normal", false, ["Bitmasks"], ""), + new Problem("ojuz", "IZhO Bank", "IZhO14_bank", "Normal", false, ["Bitmasks"], ""), + new Problem("YS", "Max Indep Set", "maximum_independent_set", "Normal", false, ["Bitmasks", "Meet in Middle"], ""), + new Problem("CF", "Wise Men", "contest/1326/problem/F2", "Very Hard", false, ["Bitmasks", "DP", "SOS"], "Solve the case where for each binary string s, a 1 means that the adjacent men know each other, and the 0 means nothing: they can know each other or not."), + ], + broken: [ + new Problem("CF", "Guards in the Storehouse", "problemset/problem/845/F", "Normal", false, [], ""), + new Problem("Plat", "Compound Escape", "949", "Very Hard", false, [], ""), + ], +}; + +<Info title="Pro Tip"> + +You can often use this to solve subtasks. + +</Info> + +## Tutorial + +<Resources> + <Resource source="PAPS" title="9.4"> </Resource> + <Resource source="CPH" title="10 (Bit Manipulation), 19.2 (Hamiltonian Paths)"> </Resource> + <Resource source="CF" title="DP Over Subsets" url="blog/entry/337"> </Resource> + <Resource source="HE" title="DP and Bit Masking" url="https://www.hackerearth.com/practice/algorithms/dynamic-programming/bit-masking/tutorial/"> </Resource> + <Resource source="CF" title="SOS Dynamic Programming" url="blog/entry/45223"> Sum over Subsets DP </Resource> +</Resources> + +## Problems + +<Problems problems={problems.general} /> + +## DP on Broken Profile + +<Resources> + <Resource source="cp-algo" title="DP on Broken Profile" url="dynamic_programming/profile-dynamics.html"> </Resource> +</Resources> + +(fill in? more probs?) + +<Problems problems={problems.broken} /> diff --git a/content/5_Plat/DP_More.mdx b/content/5_Plat/DP_More.mdx new file mode 100644 index 0000000..21dafbf --- /dev/null +++ b/content/5_Plat/DP_More.mdx @@ -0,0 +1,25 @@ +--- +id: dp-more +title: "More DP (come up with better name?)" +author: "?" +description: "?" +frequency: 1 +--- + +D&C: + +Mowing: http://www.usaco.org/index.php?page=viewproblem2&cpid=926 + +Miscellaneous Techniques: https://codeforces.com/blog/entry/47764 + +<Info title="Pro Tip"> + +There are [plenty](https://github.com/bqi343/USACO/blob/master/Contests/USACO%20Links/Division-Specific/Platinum.md) of Platinum DP problems that are not covered by this guide; we recommend that you work through these on your own. + +</Info> + +<Optional title="Application"> + +Queue w/ Two Stacks is used to remove a factor of $O(\log N)$ in [USACO Plat - Mowing Mischief](http://www.usaco.org/index.php?page=viewproblem2&cpid=926). + +</Optional> diff --git a/content/6_Plat/DP_Ranges.mdx b/content/5_Plat/DP_Ranges.mdx index 5bac3ea..1aa9d05 100644 --- a/content/6_Plat/DP_Ranges.mdx +++ b/content/5_Plat/DP_Ranges.mdx @@ -3,27 +3,29 @@ id: dp-ranges title: "Dynamic Programming on Ranges" author: Michael Cao prerequisites: - - Gold - Introduction to Dynamic Programming + - intro-dp description: Solving the problem on every contiguous subarray of the original array. frequency: 2 --- import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { general: [ new Problem("Gold", "248", "647", "Easy", false, ["Range DP"]), new Problem("CSES", "Empty String", "1080", "Normal", false, ["Range DP"]), new Problem("CF", "Zuma", "problemset/problem/607/B", "Normal", false, ["Range DP"]), - new Problem("Plat", "Greedy Pie Eaters", "972", "Normal", false, ["Range DP"]), - new Problem("Plat", "Subsequence Reversal", "698", "Normal", false, ["Range DP"]), + new Problem("Plat", "Greedy Pie Eaters", "972", "Hard", false, ["Range DP"]), + new Problem("Plat", "Subsequence Reversal", "698", "Hard", false, ["Range DP"]), ] - } }; +## Tutorial + +?? + ## Problems -<problems-list problems={metadata.problems.general} /> +<Problems problems={problems.general} /> - * TC SRM 787 500
\ No newline at end of file + * TC SRM 787 500 diff --git a/content/5_Plat/Eulerian_Tours.mdx b/content/5_Plat/Eulerian_Tours.mdx new file mode 100644 index 0000000..11f3784 --- /dev/null +++ b/content/5_Plat/Eulerian_Tours.mdx @@ -0,0 +1,37 @@ +--- +id: eulerian-tours +title: "Eulerian Tours" +author: Benjamin Qi +prerequisites: + - dfs +description: Visiting all edges of a graph exactly once. +frequency: 0 +--- + +Has not appeared on a recent USACO contest. + +import { Problem } from "../models"; + +export const problems = { + sam: [ + new Problem("CSES", "Mail Delivery (Undirected)", "1691", "Easy", false, ["Euler Tour"], ""), + new Problem("CSES", "Teleporters (Directed)", "1693", "Easy", false, ["Euler Tour"], ""), + ], + general: [ + new Problem("CSA", "Matching Substrings", "matching-substrings", "Normal", false, [], ""), + new Problem("CF", "Johnny and Megan's Necklace", "contest/1361/problem/C", "Normal", false, ["Euler Tour"], ""), + new Problem("CF", "Data Center Drama", "contest/528/problem/C", "Normal", false, ["Euler Tour"], ""), + ] +}; + +### Standard + +<Problems problems={problems.sam} /> + +### Tutorial + + - CPH (19, Path & Circuits) + +### Problems + +<Problems problems={problems.general} /> diff --git a/content/6_Plat/Eulers_Formula.mdx b/content/5_Plat/Eulers_Formula.mdx index 41282cc..c6debb1 100644 --- a/content/6_Plat/Eulers_Formula.mdx +++ b/content/5_Plat/Eulers_Formula.mdx @@ -10,10 +10,9 @@ frequency: 1 import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { e1: [ - new Problem("DMOJ", "Rainbow Gold", "apio17p1", "Hard", false, [],""), + new Problem("DMOJ", "Land of the Rainbow Gold", "apio17p1", "Hard", false, [],""), ], e2: [ new Problem("Plat", "Valleys", "950", "Hard", false, [], ""), @@ -21,17 +20,16 @@ export const metadata = { other: [ new Problem("Kattis", "Island Archipelago", "https://utipc20s.kattis.com/problems/utipc20s.islandarchipelago", "Very Hard", false, [],""), ], - } }; ## Example 1 -<problems-list problems={metadata.problems.e1} /> - -Land of Rainbow Gold +<Problems problems={problems.e1} /> ## Example 2 -<problems-list problems={metadata.problems.e2} /> +<Problems problems={problems.e2} /> + +Extension: -<problems-list problems={metadata.problems.other} />
\ No newline at end of file +<Problems problems={problems.other} />
\ No newline at end of file diff --git a/content/5_Plat/Flows.mdx b/content/5_Plat/Flows.mdx new file mode 100644 index 0000000..9a0472e --- /dev/null +++ b/content/5_Plat/Flows.mdx @@ -0,0 +1,90 @@ +--- +id: max-flow +title: "Maximum Flow" +author: Benjamin Qi +prerequisites: + - bfs +description: Introduces maximum flow as well as flow with lower bounds. +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + maxSam: [ + new Problem("CSES", "Download Speed", "1694", "Easy", false, ["Max Flow"], ""), + ], + flow: [ + new Problem("CSES", "Distinct Routes", "1711", "Easy", false, ["Max Flow"], ""), + ], + match: [ + new Problem("CSES", "School Dance", "1696", "Easy", false, ["Max Flow"], ""), + new Problem("Old Gold", "Cow Steepchase", "93", "Easy", false, ["Max Flow"], ""), + ], + dinic: [ + new Problem("SPOJ", "Fast Flow", "FASTFLOW", "Easy", false, [], ""), + new Problem("YS", "Bipartite Matching", "bipartitematching", "Easy", false, [], ""), + ], + minEx: [ + new Problem("CSES", "Coin Grid", "1709", "Easy", false, [], ""), + ], + cut: [ + new Problem("CSA", "Fashion", "fashion", "Normal", false, [], ""), + new Problem("CF", "Card Game", "problemset/problem/808/F", "Normal", false, [], ""), + new Problem("CF", "Goods Transportation", "problemset/problem/724/E", "Hard", false, [], ""), + new Problem("AC", "ARC E - MUL", "http://arc085.contest.atcoder.jp/tasks/arc085_c", "Hard", false, [], ""), + ] +}; + +## Maximum Flow + +<Problems problems={problems.maxSam} /> + +### Tutorial + +<Resources> + <Resource source="CPC" title="10 - Network Flow" url="10_graphs_3_network_flow"> </Resource> + <Resource source="CPH" title="20 - Flows & Cuts"> </Resource> +</Resources> + +### Problems + +<Problems problems={problems.flow} /> + +## Bipartite Matching + +<Problems problems={problems.match} /> + +## Dinic's Algorithm + +<Problems problems={problems.dinic} /> + +Hopcroft-Karp Bipartite Matching? + +<Optional title="Faster Flow"> + +There exist faster flow algorithms such as **Push-Relabel**. Also see the following blog post: + + - [Chilli: Dinic's with Scaling](https://codeforces.com/blog/entry/66006) + +However, the standard implementation of Dinic's is (almost) always fast enough. + +</Optional> + +## Min-Cut Max Flow + +<Problems problems={problems.minEx} /> + +(show equivalence) + +Konig's? + + - [Closure Problem](https://en.wikipedia.org/wiki/Closure_problem)? + +Dilworth stuff, antichains? + +https://maps20.kattis.com/problems/thewrathofkahn + +## Problems + +<Problems problems={problems.cut} /> diff --git a/content/6_Plat/Fracture.mdx b/content/5_Plat/Fracture.mdx index eca54c1..8272064 100644 --- a/content/6_Plat/Fracture.mdx +++ b/content/5_Plat/Fracture.mdx @@ -11,8 +11,7 @@ frequency: 1 import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { sample: [ new Problem("Plat", "Robotic Cow Herd", "674", "Normal", false, [], ""), ], @@ -21,7 +20,6 @@ export const metadata = { new Problem("DMOJ", "CCO - Shopping Plans", "cco20p6", "Very Hard", false, [], "Generalization of RoboHerd."), new Problem("YS", "K-th Shortest Walk", "k_shortest_walk", "Very Hard", false, [], "(english description?), [Relevant Paper](https://www.ics.uci.edu/~eppstein/pubs/Epp-SJC-98.pdf), Can use to solve RoboHerd!"), ], - } }; @@ -39,11 +37,11 @@ Suppose that you have a rooted tree where each vertex $i$ has a value $v_i$. Als We'll focus on the first approach. -<optional-content title="A Faster Solution"> +<Optional title="A Faster Solution"> There are ways to do this in $O(K)$ time for a binary tree if you don't need to return the values in sorted order (see [here](https://www.sciencedirect.com/science/article/pii/S0890540183710308)). -</optional-content> +</Optional> ### Generalizing @@ -85,7 +83,7 @@ for each $i\in [1,N-1]$. Every spanning tree other than the root is contained wi Overall, the runtime is $O(NMK\alpha(N))$ for storing the information about each spanning tree and $O(NK\log (NK))$ for maintaing the priority queue of objects so that we can extract the minimum. Note that with the second approach mentioned in the first section the running time would instead be $O(NMK\alpha(N)\log ans)$, which may be too slow. -<spoiler title="My Solution"> +<Spoiler title="My Solution"> ```cpp #include <bits/stdc++.h> @@ -142,12 +140,12 @@ int main() { } ``` -</spoiler> +</Spoiler> ## Robotic Cow Herd -<problems-list problems={metadata.problems.sample} /> +<Problems problems={problems.sample} /> As with the analysis, for each location you should @@ -161,7 +159,7 @@ Importantly, we should then sort the locations by their respective second-minimu Binary search on the cost $c$ of the $K$-th robot. If we can compute the costs of all robots with cost at most $c$ or say that there are more than $K$ in $O(K)$ time, then we can solve this problem in $O(N\log N+K\log \max(c))$ time (similar to "Approach 2" above). This is the approach that the first analysis solution takes, although it includes an extra $\log N$ factor due to `upper_bound`. I have removed this in my solution below. -<spoiler title="My Solution 1"> +<Spoiler title="My Solution 1"> ```cpp #include <bits/stdc++.h> @@ -220,7 +218,7 @@ int main() { } ``` -</spoiler> +</Spoiler> ### Approach 2 @@ -256,7 +254,7 @@ None of these options can result in a robot of lower cost. In general, suppose t Since there exists exactly one way to get from the cheapest robot to every possible robot, we can just use a priority queue. -<spoiler title="My Solution 2"> +<Spoiler title="My Solution 2"> ```cpp #include <bits/stdc++.h> @@ -304,8 +302,8 @@ int main() { } ``` -</spoiler> +</Spoiler> ## Other Problems -<problems-list problems={metadata.problems.general} />
\ No newline at end of file +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/5_Plat/Geo_Pri.mdx b/content/5_Plat/Geo_Pri.mdx new file mode 100644 index 0000000..fecbf4a --- /dev/null +++ b/content/5_Plat/Geo_Pri.mdx @@ -0,0 +1,58 @@ +--- +id: geo-pri +title: "Geometry Primitives" +author: Benjamin Qi +description: Basic setup for geometry problems. +--- + +import { Problem } from "../models"; + +export const problems = { + standard: [ + new Problem("YS", "Sort Points by Argument", "sort_points_by_argument", "Intro", false, [], ""), + new Problem("Kattis", "Segment Intersection", "segmentintersection", "Intro", false, [], ""), + new Problem("Kattis", "Segment Distance", "segmentdistance", "Intro", false, [], ""), + new Problem("Kattis", "Point in Polygon", "pointinpolygon", "Intro", false, [], ""), + new Problem("Kattis", "Polygon Area", "polygonarea", "Intro", false, [], ""), + ], + other: [ + new Problem("CF", "Arpa & Geo", "problemset/problem/851/B", "Intro", false, [], ""), + new Problem("CF", "Tell Your World", "problemset/problem/849/B", "Intro", false, [], ""), + new Problem("CF", "Gleb & Pizza", "problemset/problem/842/B", "Intro", false, [], ""), + new Problem("Kattis", "Max Collinear", "maxcolinear", "Easy", false, [], ""), + new Problem("Kattis", "Birthday Cake", "birthdaycake", "Easy", false, [], ""), + ] +}; + +## Primitives + +You should know basic operations like cross product and dot product. + +### Tutorial + +<Resources> + <Resource source="CF" title="C++ - std::complex" url="blog/entry/22175" starred>short description of operations</Resource> + <Resource source="CPH" title="29 - Geometry" starred>Complex #s, Points & Lines, Polygons, Distances</Resource> + <Resource source="CF" title="Point Struct" url="blog/entry/48122" starred>code, examples</Resource> + <Resource source="cp-algo" title="Geometry - Elementary Operations" url="https://cp-algorithms.com/" starred></Resource> + <Resource source="CPC" title="12 - geometry" url="12_geometry" starred>basics, polygon area, point in polygon</Resource> + <Resource source="CF" title="vlecomte - Geometry Handbook" url="blog/entry/59129" starred>some material is quite advanced</Resource> + <Resource source="TC" title="Basic Geometry Concepts Pts 1,2" url="geometry-concepts-basic-concepts">broken code format</Resource> +</Resources> + + - [My Templates](https://github.com/bqi343/USACO/tree/master/Implementations/content/geometry%20(13)/Primitives) + +### Standard Problems + +<Resources> + <Resource source="IUSACO" title="14.2 - Segment Intersection"></Resource> +</Resources> + +<Problems problems={problems.standard} /> + +### Misc Problems + +<Problems problems={problems.other} /> + + - [Racing Off Track](https://open.kattis.com/contests/acpc17open/problems/racingofftrack) (link doesn't work ...) + - [TopCoder Watchtower](https://community.topcoder.com/stat?c=problem_statement&pm=2014&rd=4685) diff --git a/content/5_Plat/HLD.mdx b/content/5_Plat/HLD.mdx new file mode 100644 index 0000000..f352860 --- /dev/null +++ b/content/5_Plat/HLD.mdx @@ -0,0 +1,51 @@ +--- +id: hld +title: "Heavy-Light Decomposition" +author: Benjamin Qi +prerequisites: + - tree-euler + - RURQ +description: Path and subtree updates and queries. +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("CSES", "Company Queries II", "1688", "Intro|Easy", false, ["LCA"], "Pure implementation; see Benq's library code, it has a function for LCA. Though this problem can be solved with binary lifting as well, you should do it with HLD to get practice."), + ], + general: [ + new Problem("Gold", "Milk Visits", "970", "Easy", false, ["HLD"], "This has a linear time solution, but it's easier to make modifications to the HLD template."), + new Problem("Gold", "Cow Land", "921", "Easy", false, ["PURS", "HLD"], ""), + new Problem("Old Gold", "Grass Planting", "102", "Intro|Easy", false, ["HLD", "PURS"], ""), + new Problem("Plat", "Disrupt", "842", "Easy", false, ["HLD"], ""), + new Problem("HR", "Subtrees & Paths", "https://www.hackerrank.com/challenges/subtrees-and-paths", "Intro|Easy", false, ["HLD", "RURQ"], "See adamant's blog."), + new Problem("YS","Vertex Set Path Composite","vertex_set_path_composite", "Intro|Normal", false, ["HLD", "SegTree"], "Function order matters! Maintain two segment trees, one for going up and the other for going down the tree."), + new Problem("CF", "Tree Queries", "contest/1254/problem/D", "Hard", false, ["HLD"], "maybe hard to see why this applies here, gives $O(N\\log N)$ while most people solved it with some factor of $\\sqrt N$"), + new Problem("CF", "Two-Paths", "contest/1000/problem/G", "Hard", false, ["HLD"], ""), + new Problem("ojuz", "JOI - Synchronization", "JOI13_synchronization", "Hard", false, ["HLD"], "$O(N\\log N)$ :D"), + new Problem("ojuz", "JOI - Cats or Dogs", "JOI18_catdog", "Very Hard", false, ["HLD"], ""), + ], +}; + +<Problems problems={problems.sample} /> + +## Tutorial + +<Resources> + <Resource source="cp-algo" title="HLD" url="https://cp-algorithms.com/graph/hld.html" starred>For an alternate implementation, see below</Resource> + <Resource source="anudeep2011" title="HLD" url="https://blog.anudeep2011.com/heavy-light-decomposition/">explains what HLD is (but incomplete & overly complicated code)</Resource> +</Resources> + +## Implementations + +<Resources> + <Resource source="CF" title="AI-Cash - HLD Implementation" url="blog/entry/22072" starred /> + <Resource source="CF" title="adamant - Easiest HLD with subtree queries" url="blog/entry/53170" starred>not complete</Resource> + <Resource source="Benq" title="Complete HLD Implementation" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/graphs%20(12)/Trees%20(10)/HLD%20(10.3).h" starred>complete implementation following the above two articles with minor modifications</Resource> +</Resources> + +## Problems + +<Problems problems={problems.general} /> diff --git a/content/5_Plat/LC.mdx b/content/5_Plat/LC.mdx new file mode 100644 index 0000000..c29351a --- /dev/null +++ b/content/5_Plat/LC.mdx @@ -0,0 +1,51 @@ +--- +id: LC +title: "LineContainer" +author: Benjamin Qi +description: Convex Containers +prerequisites: + - convex-hull +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("YS", "Line Add Get Min", "line_add_get_min", "Normal", false, [], ""), + ], + half: [ + new Problem("Kattis", "Marshland Rescues", "https://maps19.kattis.com/problems/marshlandrescues", "Normal", false, [], ""), + ], + probs: [ + new Problem("YS", "Segment Add Get Min", "segment_add_get_min", "Normal", false, [], ""), + new Problem("CSA", "Building Bridges", "building-bridges", "Normal", false, [], ""), + new Problem("TOKI", "Mall & Transportation", "https://tlx.toki.id/contests/troc-13-div-1/problems/D", "Very Hard", false, [], ""), + new Problem("Old Gold", "Fencing the Herd", "534", "Very Hard", false, [], ""), + ] +}; + +## Half-Plane Intersection + +<Resources> + <Resource source="CF" title="Blogewoosh - Half-Plane Intersection w/ Ternary Search" url="blog/entry/61710" starred></Resource> + <Resource source="Petr" title="Linear Half-Plane Intersection" url="https://petr-mitrichev.blogspot.com/2016/07/a-half-plane-week.html" starred>expected linear!</Resource> +</Resources> + +<Problems problems={problems.half} /> + +## LineContainer + +<Problems problems={problems.sample} /> + +<Resources> + <Resource source="KACTL" title="LineContainer" url="https://github.com/kth-competitive-programming/kactl/blob/master/content/data-structures/LineContainer.h" starred>code</Resource> + <Resource source="cp-algo" title="Lichao Tree" url="geometry/convex_hull_trick.html" starred> </Resource> + <Resource source="CF" title="retrograd - Half-Plane Set" url="blog/entry/61710?#comment-457662">code</Resource> +</Resources> + +## Problems + +<Problems problems={problems.probs} /> + +https://atcoder.jp/contests/arc066/tasks/arc066_d
\ No newline at end of file diff --git a/content/5_Plat/Lagrange.mdx b/content/5_Plat/Lagrange.mdx new file mode 100644 index 0000000..92af967 --- /dev/null +++ b/content/5_Plat/Lagrange.mdx @@ -0,0 +1,36 @@ +--- +id: lagrange +title: "Lagrangian Relaxation" +author: Benjamin Qi +description: "aka Aliens Trick" +prerequisites: + - convex-hull +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("Plat", "Tall Barn", "697", "Easy", false, [], ""), + ], + probs: [ + new Problem("CF","New Year & Handle Change", "contest/1279/problem/F", "Normal", false, [], ""), + new Problem("Kattis", "Blazing New Trails", "blazingnewtrails", "Normal", false, [], ""), + new Problem("ojuz", "Aliens", "IOI16_aliens", "Hard", false, [], ""), + ] +}; + +adding lambda\*smth + +<Problems problems={problems.sample} /> + +## Tutorial + +<Resources> + <Resource source="Mamnoon Siam" title="Attack on Aliens" url="https://mamnoonsiam.github.io/posts/attack-on-aliens.html"></Resource> +</Resources> + +## Problems + +<Problems problems={problems.probs} />
\ No newline at end of file diff --git a/content/5_Plat/Mat_Exp.mdx b/content/5_Plat/Mat_Exp.mdx new file mode 100644 index 0000000..c11874e --- /dev/null +++ b/content/5_Plat/Mat_Exp.mdx @@ -0,0 +1,23 @@ +--- +id: mat-exp +title: "Matrix Exponentiation" +author: "?" +description: "?" +prerequisites: + - intro-dp +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + usaco: [ + new Problem("Plat", "COWBASIC", "746", "Hard", false, [], ""), + ] +}; + +<Resources> + <Resource source="CF" url="https://codeforces.com/blog/entry/80195" title="Errichto - Matrix Exponentiation">video + problemset</Resource> +</Resources> + +<Problems problems={problems.usaco} />
\ No newline at end of file diff --git a/content/5_Plat/Merging.mdx b/content/5_Plat/Merging.mdx new file mode 100644 index 0000000..fbf68c0 --- /dev/null +++ b/content/5_Plat/Merging.mdx @@ -0,0 +1,180 @@ +--- +id: merging +title: "Small-To-Large Merging" +author: Michael Cao, Benjamin Qi +prerequisites: + - dfs + - PURS +description: "?" +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + sam: [ + new Problem("CSES", "Distinct Colors", "1139", "Intro", false, ["Merging"]), + ], + general: [ + new Problem("Silver", "Wormhole Sort", "992", "Easy", false, ["Merging"]), + new Problem("CF", "Lomsat gelral", "contest/600/problem/E", "Normal", false, ["Merging"]), + 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"], ""), + ], +}; + +## Additional Reading + +<Resources> + <Resource source="CPH" title="18.4 - Merging Data Structures"></Resource> + <Resource source="CF" title="Arpa - Sack (DSU on Tree)" url="blog/entry/44351"></Resource> + <Resource source="CF" title="tuwuna - Explaining DSU on Trees" url="blog/entry/67696"></Resource> +</Resources> + +## Merging Data Structures + +Obviously [linked lists](http://www.cplusplus.com/reference/list/list/splice/) can be merged in $O(1)$ time. But what about sets or vectors? + +<Problems problems={problems.sam} /> + +Let's consider a tree rooted at node $1$, where each node has a color. + +For each node, let's store a set containing only that node, and we want to merge the sets in the nodes subtree together such that each node has a set consisting of all colors in the nodes subtree. Doing this allows us to solve a variety of problems, such as query the number of distinct colors in each subtree. + +### Naive Solution + +Suppose that we want merge two sets $a$ and $b$ of sizes $n$ and $m$, respectively. One possiblility is the following: + +```cpp +for (int x: b) a.insert(x); +``` + +which runs in $O(m\log (n+m))$ time, yielding a runtime of $O(N^2\log N)$ in the worst case. If we instead maintain $a$ and $b$ as sorted vectors, we can merge them in $O(n+m)$ time, but $O(N^2)$ is also too slow. + +### Better Solution + +With just one additional line of code, we can significantly speed this up. + +```cpp +if (a.size() < b.size()) swap(a,b); +for (int x: b) a.insert(x); +``` + +Note that [swap](http://www.cplusplus.com/reference/utility/swap/) exchanges two sets in $O(1)$ time. Thus, merging a smaller set of size $m$ into the larger one of size $n$ takes $O(m\log n)$ time. + +**Claim:** The solution runs in $O(N\log^2N)$ time. + +**Proof:** When merging two sets, you move from the smaller set to the larger set. If the size of the smaller set is $X$, then the size of the resulting set is at least $2X$. Thus, an element that has been moved $Y$ times will be in a set of size at least $2^Y$, and since the maximum size of a set is $N$ (the root), each element will be moved at most $O(\log N$) times. + +<Spoiler title="Full Code"> + +```cpp +#include <bits/stdc++.h> + +using namespace std; + +const int MX = 200005; + +vector<int> adj[MX]; set<int> col[MX]; long long ans[MX]; +void dfs(int v, int p){ + for(int e : adj[v]){ + if(e != p){ + dfs(e, v); + if(col[v].size() < col[e].size()){ + swap(col[v], col[e]); + } + for(int a : col[e]){ + col[v].insert(a); + } + col[e].clear(); + } + } + ans[v] = col[v].size(); +} +int main() { + ios::sync_with_stdio(false); + cin.tie(0); + int n; cin >> n; + for(int i = 0; i < n; i++){ + int x; cin >> x; + col[i].insert(x); + } + for(int i = 0; i < n - 1; i++){ + int u,v; cin >> u >> v; + u--; v--; + adj[u].push_back(v); adj[v].push_back(u); + } + dfs(0,-1); + for(int i = 0; i < n; i++){ + cout << ans[i] << " "; + } +} +``` + +</Spoiler> + +## Generalizing + +A set doesn't have to be an `std::set`. Many data structures can be merged, such as `std::map` or `std:unordered_map`. However, `std::swap` doesn't necessarily work in $O(1)$ time; for example, swapping two [arrays](http://www.cplusplus.com/reference/array/array/swap/) takes time linear in the sum of the sizes of the arrays, and the same goes for indexed sets. For two indexed sets `a` and `b` we can use `a.swap(b)` in place of `swap(a,b)` (documentation?). + +## Problems + +<Problems problems={problems.general} /> + +<Spoiler title="Solution to Promotion Counting"> + +```cpp +#include <bits/stdc++.h> +#include <ext/pb_ds/tree_policy.hpp> +#include <ext/pb_ds/assoc_container.hpp> + +using namespace std; +using namespace __gnu_pbds; + +template<class T> using Tree = tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>; + +const int MX = 1e5+5; +#define sz(x) (int)(x).size() + +int N, a[MX], ind[MX], ans[MX], ret; +vector<int> child[MX]; +Tree<int> d[MX]; + +void comb(int a, int b) { + if (sz(d[a]) < sz(d[b])) d[a].swap(d[b]); + for (int i: d[b]) d[a].insert(i); +} + +void dfs(int x) { + ind[x] = x; + for (int i: child[x]) { + dfs(i); + comb(x,i); + } + ans[x] = sz(d[x])-d[x].order_of_key(a[x]); + d[x].insert(a[x]); +} + +int main() { + freopen("promote.in","r",stdin); + freopen("promote.out","w",stdout); + cin >> N; for (int i = 1; i <= N; ++i) cin >> a[i]; + for (int i = 2; i <= N; ++i) { + int p; cin >> p; + child[p].push_back(i); + } + dfs(1); + for (int i = 1; i <= N; ++i) cout << ans[i] << "\n"; +} +``` + +</Spoiler> + +(also: same solution w/o indexed set) + +<Optional title="Faster Merging"> + +It's easy to merge two sets of sizes $n\ge m$ in $O(n+m)$ or $(m\log n)$ time, but sometimes $O\left(m\log \left(1+\frac{n}{m}\right)\right)$ can be significantly better than both of these. Check "Advanced - Treaps" for more details. Also see [this link](https://codeforces.com/blog/entry/49446) regarding merging segment trees. + +</Optional>
\ No newline at end of file diff --git a/content/5_Plat/Offline_Con.mdx b/content/5_Plat/Offline_Con.mdx new file mode 100644 index 0000000..2685d06 --- /dev/null +++ b/content/5_Plat/Offline_Con.mdx @@ -0,0 +1,38 @@ +--- +id: offline-con +title: "Offline Dynamic Connectivity" +author: Benjamin Qi +prerequisites: + - dsu +description: "?" +frequency: 0 +--- + +import { Problem } from "../models"; + +export const problems = { + rollback: [ + new Problem("YS", "Persistent Union Find", "persistent_unionfind", "Normal", false, [], ""), + new Problem("YS", "Vertex Add Component Sum", "dynamic_graph_vertex_add_component_sum", "Hard", false, [], ""), + new Problem("CF", "Edu F - Extending Set of Points", "contest/1140/problem/F", "Hard", false, [], ""), + ], + ins: [ + new Problem("Old Gold", "Fencing the Herd", "534", "Hard", false, [], ""), + ], +}; + +### DSU With Rollback + +no path compression + +(tutorial?) + +<Problems problems={problems.rollback} /> + +<!-- ## Dynamic Insertion + +mention sqrt + +(online Aho-Corasick) + +<Problems problems={problems.ins} /> -->
\ No newline at end of file diff --git a/content/5_Plat/RURQ.mdx b/content/5_Plat/RURQ.mdx new file mode 100644 index 0000000..aeb9395 --- /dev/null +++ b/content/5_Plat/RURQ.mdx @@ -0,0 +1,80 @@ +--- +id: RURQ +title: "Range Update Range Query" +author: Benjamin Qi +prerequisites: + - PURS +description: Lazy updates on segment trees and two binary indexed trees in conjunction. +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + bitSample: [ + new Problem("SPOJ", "Horrible Queries", "HORRIBLE", "Easy", false, ["BIT-Range"], ""), + ], + bitProb: [ + new Problem("CSES", "Polynomial Queries", "1736", "Easy", false, ["BIT-Range"], "equivalent to https://dmoj.ca/problem/acc3p4"), + ], + lazySample: [ + new Problem("CSES", "Range Updates & Sums", "1735", "Easy", false, ["Lazy SegTree"], ""), + ], + lazySegTree: [ + new Problem("YS", "Range Affine Range Sum", "range_affine_range_sum", "Easy", false, ["Lazy SegTree"], ""), + new Problem("Plat", "Counting Haybales", "578", "Easy", false, ["Lazy SegTree"], ""), + new Problem("Old Gold", "The Lazy Cow", "418", "Easy", false, ["Lazy SegTree"], ""), + new Problem("Plat", "Snow-Cow", "973", "Normal", false, ["Lazy SegTree"], "Can use lazy seg in place of two BITs"), + ], + lazySegCnt: [ + new Problem("CSES", "Area of Rectangles", "1741", "Normal", false, ["Lazy SegTree"], ""), + new Problem("HR", "Strange Tree", "https://www.hackerrank.com/contests/openbracket-2017/challenges/special-path-on-a-strange-tree/problem", "Very Hard", false, ["Lazy SegTree"]), + ], + segTreeBeats: [ + new Problem("YS", "Range Chmin Chmax Add Range Sum", "range_chmin_chmax_add_range_sum", "Very Hard", false, ["SegTreeBeats"], ""), + ], +}; + +## BIT Revisited + +<Problems problems={problems.bitSample} /> + +Binary Indexed Trees can support range increments in addition to range sum queries. + +### Tutorial + +<Resources> + <Resource source="GFG" title="Range Update Point Query" url="binary-indexed-tree-range-updates-point-queries/"></Resource> + <Resource source="GFG" title="Range Update Range Query" url="binary-indexed-tree-range-update-range-queries/"></Resource> +</Resources> + +[My Implementation](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BITrange.h) + +### Problems + +<Problems problems={problems.bitProb} /> + +## Lazy Segment Tree + +<Problems problems={problems.lazySample} /> + +### Tutorial + +<Resources> + <Resource source="CPH" title="28.1 - Segment Trees Revisited" starred>short description</Resource> + <Resource source="CSA" title="Segment Trees" url="segment_trees" starred>interactive</Resource> + <Resource source="cp-algo" title="Segment Tree" url="data_structures/segment_tree.html" starred>adding on segments, assigning</Resource> + <Resource source="CF" title="Efficient and easy segment trees" url="blog/entry/18051">code is more confusing than recursive version</Resource> +</Resources> + +### Problems + +<Problems problems={problems.lazySegTree} /> + +## Lazy Segment Tree - Counting Minimums + +use segment tree that keeps track of minimum and # of minimums + +(describe) + +<Problems problems={problems.lazySegCnt} />
\ No newline at end of file diff --git a/content/5_Plat/SCC.mdx b/content/5_Plat/SCC.mdx new file mode 100644 index 0000000..7bbc6b8 --- /dev/null +++ b/content/5_Plat/SCC.mdx @@ -0,0 +1,64 @@ +--- +id: SCC +title: "Strongly Connected Components" +author: Benjamin Qi +prerequisites: + - toposort + - BCC-2CC +description: +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + ex: [ + new Problem("CSES", "Planets & Kingdoms", "1683", "Easy", false, [], ""), + ], + general: [ + new Problem("CSES", "Coin Collector", "1686", "Easy", false, [], ""), + new Problem("Old Gold", "Grass Cownoisseur", "516", "Normal", false, [], ""), + new Problem("CF", "Catowice City", "contest/1239/problem/D", "Normal", false, [], ""), + new Problem("POI", "Festival", "https://szkopul.edu.pl/problemset/problem/p9uJo01RR9ouMLLAYroFuQ-7/site/?key=statement", "Hard", false, [], ""), + new Problem("Kattis", "Proving Equivalences", "equivalences", "Hard", false, [], ""), + new Problem("CSES", "New Flight Routes", "1685", "Hard", false, [], ""), + ], + satEx: [ + new Problem("CSES", "Giant Pizza", "1684", "Normal", false, [], "") + ], +}; + +## SCCs + +<Problems problems={problems.ex} /> + +### Tutorial + + - Wikipedia + - [Tarjan](https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm) + - [Kosaraju](https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm) + +<Resources> + <Resource source="CPH" title="17 - Strong Connectivity"></Resource> + <Resource source="CPC" title="7 - Graphs 1" url="07_graphs_1"></Resource> +</Resources> + +(impls) + +### Problems + +<Problems problems={problems.general} /> + +## 2-SAT + +<Problems problems={problems.satEx} /> + +(impl) + +### Tutorial + +<Resources> + <Resource source="CF" title="2-SAT" url="blog/entry/16205"></Resource> +</Resources> + +(KACTL at most one?) diff --git a/content/5_Plat/SPneg.mdx b/content/5_Plat/SPneg.mdx new file mode 100644 index 0000000..2dccf2d --- /dev/null +++ b/content/5_Plat/SPneg.mdx @@ -0,0 +1,65 @@ +--- +id: sp-neg +title: "Shortest Paths with Negative Edge Weights" +author: Benjamin Qi +prerequisites: + - sp +description: Applications of Bellman-Ford. +frequency: 0 +--- + +import { Problem } from "../models"; + +export const problems = { + sam: [ + new Problem("Kattis", "SSSP Negative", "shortestpath3", "Easy", false, [], ""), + new Problem("Kattis", "APSP (with negative weights)", "allpairspath", "Easy", false, [], ""), + new Problem("CSES", "(Negative) Cycle Finding", "1197", "Easy", false, [], ""), + ], + probs: [ + new Problem("SPOJ", "Arbitrage", "ARBITRAG", "Easy", false, [], ""), + new Problem("CSES", "High Score", "1673", "Easy", false, [], ""), + ], + linear: [ + new Problem("ojuz", "Restore Array", "RMI19_restore", "Normal", false, [], "similar to [Art](https://codeforces.com/gym/102394/problem/A)"), + ], +}; + +<Problems problems={problems.sam} /> + +<br/> + +### Tutorial + +<Resources title="SSSP Negative"> + <Resource source="CPH" title="13.1"></Resource> + <Resource source="cp-algo" title="Bellman-Ford" url="graph/bellman_ford.html"></Resource> + <Resource source="cp-algo" title="Finding Negative Cycle" url="finding-negative-cycle-in-graph.html"></Resource> + <Resource source="TC" title="Intro to Graphs Section 3" url="introduction-to-graphs-and-their-data-structures-section-3/"></Resource> +</Resources> + +Can also use [Shortest Path Faster Algorithm](https://en.wikipedia.org/wiki/Shortest_Path_Faster_Algorithm) or modify Dijkstra slightly (though the same running time bound no longer applies). + +(code)? + +### Problems + +<Problems problems={problems.probs} /> + +## Simple Linear Programming + +You can also use shortest path algorithms to solve the following problem (a very simple [linear program](https://en.wikipedia.org/wiki/Linear_programming)). + +> Given variables $x_1,x_2,\ldots,x_N$ with constraints in the form $x_i-x_j\ge c$, compute a feasible solution. + +<Resources> + <Resource source="MIT" title="Slides from Intro to Algorithms" url="https://www.cs.rit.edu/~spr/COURSES/ALG/MIT/lec18.pdf">Linear Programming Trick</Resource> +</Resources> + +**Example:** Timeline (USACO Camp) + + - equivalent to [Timeline (Gold)](http://www.usaco.org/index.php?page=viewproblem2&cpid=1017) except $N,C\le 5000$ and negative values of $x$ are possible. + +### Problems + +<Problems problems={problems.linear} /> diff --git a/content/5_Plat/Seg_Ext.mdx b/content/5_Plat/Seg_Ext.mdx new file mode 100644 index 0000000..78a8058 --- /dev/null +++ b/content/5_Plat/Seg_Ext.mdx @@ -0,0 +1,71 @@ +--- +id: seg-ext +title: "More Applications of Segment Tree" +author: Benjamin Qi +prerequisites: + - PURS +description: "?" +frequency: 3 +--- + +import { Problem } from "../models"; + +export const problems = { + walkSam: [ + new Problem("CSES", "Hotel Queries", "1143", "Easy", false, ["PURQ"], "walk"), + ], + walk: [ + new Problem("Old Gold", "Seating", "231", "Normal", false, [], "walk on max segtree"), + new Problem("Plat", "Balancing", "624", "Normal", false, [], "walk"), + ], + combSam: [ + new Problem("CSES", "Subarray Sum Queries", "1190", "Normal", false, ["PURQ"], "comb"), + ], + comb: [ + new Problem("Old Gold", "Marathon", "495", "Easy", false, [], "comb"), + new Problem("Old Gold", "Optimal Milking", "365", "Normal", false, [], "comb"), + new Problem("POI", "Cards", "https://szkopul.edu.pl/problemset/problem/qpsk3ygf8MU7D_1Es0oc_xd8/site/?key=statement", "Normal", false, [], "comb"), + new Problem("Plat", "High Card Low Card", "577", "Normal", false, ["PURQ", "Greedy"], "comb"), + ], + waveletSam: [ + new Problem("YS", "Range K-th Smallest", "range_kth_smallest", "Normal", false, ["Wavelet"], ""), + ], + wavelet: [ + new Problem("Kattis", "Easy Query", "easyquery", "Hard", false, ["Wavelet"], ""), + new Problem("DMOJ", "Ninjaclasher's Wrath 2", "globexcup19s4", "Hard", false, ["Wavelet"], ""), + ], +}; + +## Walking on a Segment Tree + +<Problems problems={problems.walkSam} /> + +You want to support queries of the following form on an array $a_1,\ldots,a_N$ (along with point updates). + +> Find the first $i$ such that $a_i\ge x$. + +Of course, you can do this in $O(\log^2N)$ time with a max segment tree and binary searching on the first $i$ such that $\max(a_1,\ldots,a_i)\ge x$. But try to do this in $O(\log N)$ time. + +<Problems problems={problems.walk} /> + +## Combining + +<Problems problems={problems.combSam} /> + +(solution to above problem) + +<Problems problems={problems.comb} /> + +## Wavelet Tree + +<Problems problems={problems.waveletSam} /> + +### Tutorial + +<Resources> + <Resource source="CF" title="Intro to New DS: Wavelet Trees" url="blog/entry/52854"></Resource> +</Resources> + +### Problems + +<Problems problems={problems.wavelet} /> diff --git a/content/5_Plat/Slope.mdx b/content/5_Plat/Slope.mdx new file mode 100644 index 0000000..a7948b0 --- /dev/null +++ b/content/5_Plat/Slope.mdx @@ -0,0 +1,368 @@ +--- +id: slope +title: "Slope Trick" +author: Benjamin Qi +prerequisites: + - intro-dp + - convex-hull +description: "Slope trick refers to a way to manipulate piecewise linear convex functions. Includes a simple solution to USACO Landscaping." +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + ex: [ + new Problem("CF", "Problem Without a Legend", "contest/713/problem/C", "Easy", false, ["Slope Trick"], ""), + ], + buy: [ + new Problem("CF", "Buy Low Sell High", "contest/866/problem/D", "Easy", false, ["Slope Trick"], ""), + ], + potatoes: [ + new Problem("ojuz", "LMIO - Potatoes & Fertilizers", "LMIO19_bulves", "Normal", false, ["Slope Trick"], "[Equivalent Problem](https://atcoder.jp/contests/kupc2016/tasks/kupc2016_h)"), + ], + landscaping: [ + new Problem("Plat", "Landscaping", "650", "Hard", false, ["Slope Trick"], "Equivalent Problem: GP of Wroclaw 20 J"), + ], + general: [ + new Problem("CF", "Bookface", "gym/102576/problem/C", "Normal", false, ["Slope Trick"], ""), + new Problem("CC", "CCDSAP Exam", "CCDSAP", "Normal", false, ["Slope Trick"], ""), + new Problem("CF", "Farm of Monsters", "gym/102538/problem/F", "Hard", false, ["Slope Trick"], ""), + new Problem("CF", "Moving Walkways", "contest/1209/problem/H", "Hard", false, ["Slope Trick"], ""), + new Problem("ojuz", "APIO - Fireworks", "APIO16_fireworks", "Hard", false, ["Slope Trick", "Small to Large"], ""), + new Problem("CF", "April Fools' Problem", "contest/802/problem/O", "Very Hard", false, ["Slope Trick"], "binary search on top of slope trick"), + new Problem("ICPC World Finals", "Conquer the World", "https://icpc.kattis.com/problems/conquertheworld", "Very Hard", false, ["Slope Trick", "Small to Large"], "ICPC world finals, 0 solves in contest - \"Potatoes\" on tree!!"), + ], +}; + +## Tutorials + +<Resources> + <Resource source="CF" title="zscoder - Slope Trick" url="blog/entry/47821">3 problems using this trick</Resource> + <Resource source="CF" title="Kuroni - Slope Trick Explained" url="blog/entry/77298">clarifying the above and another example problem</Resource> +</Resources> + +From the latter link (modified): + +> Slope trick is a way to represent a function that satisfies the following conditions: +> +> - It can be divided into multiple sections, where each section is a linear function (usually) with an integer slope. +> - It is a convex/concave function. In other words, the slope of each section is non-decreasing or non-increasing when scanning the function from left to right. + +It's generally applicable as a DP optimization. + +<Info title="Pro Tip"> + +Usually you can come up with a slower (usually $O(N^2)$) DP first and then optimize it to $O(N\log N)$ with slope trick. + +</Info> + +The rest of this module assumes that you know the basic idea of this trick. In particular, you should be at least somewhat familiar with the $O(N\log N)$ time solution to the first problem in zscoder's tutorial: + +<Problems problems={problems.ex} /> + +It's ok if you found the explanations confusing; the example below should help clarify. + +## Buy Low Sell High + +<Problems problems={problems.buy} /> + +### Slow Solution + +Let $dp[i][j]$ denote the maximum amount of money you can have on day $i$ if you have exactly $j$ shares of stock on that day. The final answer will be $dp[N][0]$. This solution runs in $O(N^2)$ time. + +<Spoiler title="Slow Code"> + +```cpp + +vector<vl> dp = {{0}}; +int N; + +int main() { + re(N); + F0R(i,N) { + int x; re(x); + dp.pb(vl(i+2,-INF)); + F0R(j,i+1) { + ckmax(dp.bk[j+1],dp[sz(dp)-2][j]-x); + ckmax(dp.bk[j],dp[sz(dp)-2][j]); + if (j) ckmax(dp.bk[j-1],dp[sz(dp)-2][j]+x); + } + } + int cnt = 0; + trav(t,dp) { + pr("dp[",cnt++,"] = "); + pr('{'); + F0R(i,sz(t)) { + if (i) cout << ", "; + cout << setw(3) << t[i]; + } + ps('}'); + } +} +``` + +</Spoiler> + +If we run this on the first sample case, then we get the following table: + +``` +Input: + +9 +10 5 4 7 9 12 6 2 10 + +Output: + +dp[0] = { 0} +dp[1] = { 0, -10} +dp[2] = { 0, -5, -15} +dp[3] = { 0, -4, -9, -19} +dp[4] = { 3, -2, -9, -16, -26} +dp[5] = { 7, 0, -7, -16, -25, -35} +dp[6] = { 12, 5, -4, -13, -23, -35, -47} +dp[7] = { 12, 6, -1, -10, -19, -29, -41, -53} +dp[8] = { 12, 10, 4, -3, -12, -21, -31, -43, -55} +dp[9] = { 20, 14, 7, -2, -11, -21, -31, -41, -53, -65} +``` + +However, the DP values look quite special! Specifically, let +$$ +dif[i][j]=dp[i][j]-dp[i][j+1]\ge 0. +$$ +Then $dif[i][j]\le dif[i][j+1]$ for all $j\ge 0$. In other words, $dp[i][j]$ as a function of $j$ is **concave down**. + +### Full Solution + +<Spoiler title="Explanation"> + +We'll process the shares in order. Suppose that we are currently considering the $i$-th day, where shares are worth $p_i$. We can replace (buy or sell a share) in the statement with (buy, then sell somewhere between 0 and 2 shares). + + - If we currently have $j$ shares and overall balance $b$, then after buying, $j$ increases by one and $b$ decreases by $p_i$. So we set $dp[i][j]=dp[i-1][j-1]-p_i$ for all $j$. Note that the differences between every two consecutive elements of $dp[i]$ have not changed. + + - If we choose to sell a share, this is equivalent to setting $dp[i][j]=\max(dp[i][j],dp[i][j+1]+p_i)$ for all $j$ at the same time. By the concavity condition, $dp[i][j]=dp[i][j+1]+p_i$ will hold for all $j$ less than a certain threshold while $dp[i][j]$ will remain unchanged for all others. So this is equivalent to inserting $p_i$ into the list of differences while maintaining the condition that the differences are in sorted order. + + - So choosing to sell between 0 and 2 shares is represented by adding $p_i$ to the list of differences two times. After that, we should pop the smallest difference in the list because we can't end up with a negative amount of shares. + +**Example:** consider the transition from `dp[4]` to `dp[5]`. Note that $p_5=9$. + +Start with: + +``` +dp[4] = { 3, -2, -9, -16, -26} +dif[4] = { 5, 7, 7, 10} +``` + +After buying one share, $9$ is subtracted from each value and they are shifted one index to the right. + +``` +dp[5] = { x, -6, -11, -18, -25, -35} +dif[5] = { x, 5, 7, 7, 10} +``` + +Then we can choose to sell one share at price $9$. The last two DP values remain the same while the others change. + +``` +dp[5] = { 3, -2, -9, -16, -25, -35} +dif[5] = { 5, 7, 7, 9, 10} +``` + +Again, we can choose to sell one share at price $9$. The last three DP values remain the same while the others change. $dif$ is still in increasing order! + +``` +dp[5] = { 7, 0, -7, -16, -25, -35} +dif[5] = { 7, 7, 9, 9, 10} +``` + +(insert diagrams) + +</Spoiler> + +<Spoiler title="My Code"> + +The implementation is quite simple; maintain a priority queue representing $dif[i]$ that allows you to pop the minimum element. After adding $i$ elements, $ans$ stores the current value of $dp[i][i]$. At the end, you add all the differences in $dif[N]$ to go from $dp[N][N]$ to $dp[N][0]$. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + int N; cin >> N; + priority_queue<int,vector<int>,greater<int>> pq; + long long ans = 0; + for (int i = 0; i < N; ++i) { + int p; cin >> p; ans -= p; + pq.push(p); pq.push(p); pq.pop(); + } + for (int i = 0; i < N; ++i) { + ans += pq.top(); + pq.pop(); + } + cout << ans << "\n"; +} +``` + +</Spoiler> + +### Extension + +*Stock Trading (USACO Camp)*: What if your amount of shares can go negative, but you can never have more than $L$ shares or less than $-L$? + +## Potatoes & Fertilizers + +<Problems problems={problems.potatoes} /> + +### Simplifying the Problem + +Instead of saying that moving fertilizer from segment $i$ to segment $j$ costs $|i-j|$, we'll say that it costs $1$ to move fertilizer from a segment to an adjacent segment. + +Let the values of $a_1,a_2,\ldots,a_N$ after all the transfers be $a_1',a_2',\ldots,a_N'$. If we know this final sequence, how much did the transfers cost (in the best case scenario)? It turns out that this is just + +$$ +C=\sum_{i=1}^{N-1}\left|\sum_{j=1}^i(a_j-a_j')\right|. +$$ + +We can show that this is a lower bound and that it's attainable. The term $D=\sum_{j=1}^i(a_j-a_j')$ denotes the number of units of fertilizer that move from segment $i$ to segment $i+1$. Namely, if $D$ is positive then $D$ units of fertilizer moved from segment $i$ to segment $i+1$; otherwise, $-D$ units of fertilizer moved in the opposite direction. Note that it is never optimal to have fertilizer moving in both directions. + +Let $dif_i=a_i-b_i$ and define $d_j=\sum_{i=1}^jdif_i$ for each $0\le j\le N$. Similarly, define $dif_i'=a_i'-b_i$ and $d_j'=\sum_{i=1}^jdif_i'$. Since we want $dif_i'\ge 0$ for all $i$, we should have $d_0=d_0'\le d_1'\le \cdots\le d_N'=d_N.$ Conversely, every sequence $(d_0',d_1',\ldots,d_N')$ that satisfies this property corresponds to a valid way to assign values of $(a_1',a_2',\ldots,a_N')$. + +Now you can verify that $C=\sum_{i=1}^{N-1}|d_i-d_i'|$. This makes sense since moving one unit of fertilizer one position is equivalent to changing one of the $d_i$ by one (although $d_0,d_N$ always remain the same). + +### Slow Solution + +For each $0\le i\le N$ and $0\le j\le d_N$, let $dp[i][j]$ be the minimum cost to determine $d_0',d_1',\ldots,d_i'$ such that $d_i'\le j$. Note that by definition, $dp[i][j]\ge dp[i][j+1]$. We can easily calculate these values in $O(N\cdot d_N)$ time. + +### Full Solution + +<Spoiler title="Explanation"> + +Similar to before, this DP is concave up for a fixed $i$! Given a piecewise linear function $f_i(x)$ that takes as input $x$ and outputs $dp[i][x]$, we need to support the following two operations to transform this function into $f_{i+1}$. + + - Add $|x-k|$ to the function for some $k$ + - Set $f(x)=\min(f(x),f(x-1))$ for all $x$ + +Again, these can be done with a priority queue. Instead of storing the consecutive differences, we store the points where the slope of the piecewise linear function changes by one. + + - The first operation corresponds to inserting $k$ into the priority queue two times because the slope increases by two at $x=k$. + - The latter operation just corresponds to removing the greatest element of the priority queue. + +This solution runs in $O(N\log N)$ time. + +</Spoiler> + +<Spoiler title="My Code"> + +```cpp +#include <bits/stdc++.h> +using namespace std; + +typedef long long ll; + +int N; +ll fst = 0; // value of DP function at 0 +priority_queue<ll> points; // points where DP function changes slope + +int main() { + cin >> N; + vector<ll> dif(N+1); + for (int i = 1; i <= N; ++i) { + int a,b; cin >> a >> b; + dif[i] = a-b+dif[i-1]; + } + assert(dif[N] >= 0); // assume solution exists + for (int i = 1; i < N; ++i) { + if (dif[i] < 0) fst -= dif[i], dif[i] = 0; + fst += dif[i]; + points.push(dif[i]); points.push(dif[i]); + points.pop(); + } + while (points.size()) { + ll a = points.top(); points.pop(); + fst -= min(a,dif[N]); + } + cout << fst << "\n"; +} +``` + +</Spoiler> + +## USACO Landscaping + +<Problems problems={problems.landscaping} /> + +This looks similar to the previous task (we're moving dirt instead of fertilizer), so it's not too hard to guess that slope trick is applicable. + +### Slow Solution + +Let $dp[i][j]$ equal the number of ways to move dirt around the first $i$ flowerbeds such that the first $i-1$ flowerbeds all have the correct amount of dirt while the $i$-th flowerbed has $j$ extra units of dirt (or lacks $-j$ units of dirt if $j$ is negative). The answer will be $dp[N][0]$. + +### Full Solution + +<Spoiler title="Explanation"> + +This DP is concave up for any fixed $i$. To get $dp[i+1]$ from $dp[i]$ we must be able to support the following operations. + + - Shift the DP curve $A_i$ units to the right. + - Shift the DP curve $B_i$ units to the left. + - Add $Z\cdot |j|$ to $DP[j]$ for all $j$. + - Set $DP[j] = \min(DP[j],DP[j-1]+X)$ and $DP[j] = \min(DP[j],DP[j+1]+Y)$ for all $j$. + +As before, it helps to look at the differences $dif[j]=DP[j+1]-DP[j]$ instead. We'll maintain separate deques for $dif$ depending on whether $j < 0$ or $j\ge 0$. We'll call these the left and right deques, respectively. + + - The first two operations correspond to repeatedly popping the last element off of the left deque and adding it to the front of the right deque (or vice versa, depending on the direction of the shift). + - The third operation corresponds to subtracting $Z$ from all elements of the left deque and adding $Z$ to all elements of the right deque. + - The last operation corresponds to setting $dif[j]=\max(dif[j],-Y)$ for all $j < 0$ + and $dif[j] = \min(dif[j],X)$ for all $j\ge 0$. + +We can implement the last operation by updating all of the differences in the deques "lazily." This solution runs in $O(\sum A_i+\sum B_i)$ time. + +</Spoiler> + +<Spoiler title="My Solution"> + +```cpp +#include <bits/stdc++.h> + +using namespace std; + +int N,X,Y,Z; +int difl, difr; // "lazy" update +deque<int> L, R; +long long ans; + +void rig() { // shift right A, so origin moves left + if (L.size() == 0) L.push_back(-Y-difl); + int t = L.back()+difl; L.pop_back(); + t = max(t,-Y); ans -= t; + R.push_front(t-difr); +} + +void lef() { // shift left B, so origin moves right + if (R.size() == 0) R.push_front(X-difr); + int t = R.front()+difr; R.pop_front(); + t = min(t,X); ans += t; + L.push_back(t-difl); +} + +int main() { + freopen("landscape.in","r",stdin); + freopen("landscape.out","w",stdout); + cin >> N >> X >> Y >> Z; + for (int i = 0; i < N; ++i) { + int A,B; cin >> A >> B; + for (int j = 0; j < A; ++j) rig(); // or we can just do |A-B| shifts in one direction + for (int j = 0; j < B; ++j) lef(); + difl -= Z, difr += Z; // adjust slopes differently for left and right of j=0 + } + cout << ans << "\n"; +} +``` +</Spoiler> + +### Extension + +We can solve this problem when $\sum A_i+\sum B_i$ is not so small with lazy balanced binary search trees. + +## Problems + +<Problems problems={problems.general} />
\ No newline at end of file diff --git a/content/5_Plat/Sqrt.mdx b/content/5_Plat/Sqrt.mdx new file mode 100644 index 0000000..7b88278 --- /dev/null +++ b/content/5_Plat/Sqrt.mdx @@ -0,0 +1,59 @@ +--- +id: sqrt +title: "Square Root Decomposition" +author: Benjamin Qi +description: "?" +frequency: 1 +--- +import { Problem } from "../models"; + +export const problems = { + fst: [ + new Problem("CF", "Tree Queries", "contest/1254/problem/D", "Hard", false, [], ""), + new Problem("wcipeg", "COI 08-Otoci", "https://wcipeg.com/problem/coi08p2", "Hard", false, ["HLD"], "Editorial rebuilds HLD after certain # of updates ..."), + new Problem("Old Gold", "Fencing the Herd", "534", "Very Hard", false, ["Convex hull"], ""), + new Problem("DMOJ", "Fluid Dynamics", "dmopc19c7p7", "Very Hard", false, [], ""), + new Problem("Plat", "At Large", "793", "Very Hard", false, [], ""), + ], + block: [ + new Problem("DMOJ", "Arithmetic Subtrees", "wac1p7", "Very Hard", false, [], ""), + ], + other: [ + new Problem("Plat", "Train Tracking", "841", "Insane", false, [], ""), + ] +}; + +<IncompleteSection /> + +## On Line + +processing sqrt queries / updates at a time + +partitioning into sqrt blocks (each block can have some sort of data structure ...) + +optimization tips? (ez to get TLE with bad constant ..., can get AC with suboptimal complexity ...) + +<Problems problems={problems.fst} /> + +## Mo's + +<Resources> + <Resource source="CF" title="Mo's Algorithm" url="blog/entry/7383"></Resource> + <Resource source="CF" title="Mo's on Trees" url="blog/entry/43230"></Resource> +</Resources> + +[A2OJ](https://a2oj.com/category?ID=318) + +## Other + +### Block Tree + +<Problems problems={problems.block} /> + +<Resources> + <Resource source="CF" title="Block Tree" url="blog/entry/46843"></Resource> +</Resources> + +### Train Tracking + +<Problems problems={problems.other} /> diff --git a/content/5_Plat/String_Search.mdx b/content/5_Plat/String_Search.mdx new file mode 100644 index 0000000..5ad0126 --- /dev/null +++ b/content/5_Plat/String_Search.mdx @@ -0,0 +1,135 @@ +--- +id: string-search +title: "String Searching" +author: Benjamin Qi, Siyong Huang +prerequisites: + - dfs +description: Knuth-Morris-Pratt and Z Algorithms (and a few more related topics). +frequency: 1 +--- + +export const problems = { + 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", "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, [], ""), + ] +}; + +## General + +<Resources> + <Resource source="CPC" title="11 - Strings" url="11_strings">String Matching, KMP, Tries</Resource> +</Resources> + +# Single String + +## 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> + <Resource source="GFG" title="KMP Algorithm" url="searching-for-patterns-set-2-kmp-algorithm"></Resource> + <Resource source="TC" title="String Searching" url="introduction-to-string-searching-algorithms"></Resource> +</Resources> + +<Problems problems={problems.KMP} /> + +## Z Algorithm + +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> + <Resource source="CPH" title="26.4 - Z-algorithm"></Resource> + <Resource source="CF" title="Z Algorithm" url="blog/entry/3107"></Resource> +</Resources> + +<Problems problems={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={problems.mana} /> + +# Multiple Strings + +## Tries + +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> + <Resource source="CF" title="Algorithm Gym" url="blog/entry/15729"></Resource> + <Resource source="PAPS" title="14.1 - Tries"></Resource> +</Resources> + +<Problems problems={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={problems.aho} /> + +## Palindromic Tree + +pretty similar to KMP + +<Resources> + <Resource source="CF" title="adamant - Palindromic Tree" url="blog/entry/13959"></Resource> +</Resources> + +<Problems problems={problems.pal} /> + +(+ DMOJ thing which I can't find right now ...)
\ No newline at end of file diff --git a/content/5_Plat/Suffix_Array.mdx b/content/5_Plat/Suffix_Array.mdx new file mode 100644 index 0000000..3059512 --- /dev/null +++ b/content/5_Plat/Suffix_Array.mdx @@ -0,0 +1,79 @@ +--- +id: suffix-array +title: "Suffix Array" +author: Benjamin Qi +description: "Quickly Sorting Suffixes of a String (and Applications)" +prerequisites: + - string-hashing +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + sample: [ + new Problem("YS", "Suffix Array", "suffixarray", "Easy", false, [], ""), + ], + lcpSam: [ + new Problem("YS", "# of Substrings", "number_of_substrings", "Easy", false, [], ""), + ], + lcp: [ + new Problem("Plat", "Standing Out from the Herd", "768", "Hard", false, [], ""), + new Problem("CF", "Two Prefixes", "contest/1090/problem/J", "Hard", false, [], ""), + ], + burSam: [ + new Problem("CSES", "String Transform", "1113", "Easy", false, [], ""), + ], + runSam: [ + new Problem("YS", "Run Enumerate", "runenumerate", "Hard", false, [], ""), + ] +}; + +## Resources + +<Resources> + <Resource source="CF" title="Suffix Array" url="edu/course/2/lesson/2" starred>Videos & Practice Problems</Resource> + <Resource source="cp-algo" title="Suffix Array - Definition & Construction" url="suffix-array.html" starred></Resource> + <Resource source="CPC" title="11 - Strings (Suffix Array)" url="11_strings"></Resource> +</Resources> + +## Suffix Array + +<Problems problems={problems.sample} /> + +### Implementations + +<Resources> + <Resource source="Benq" title="Suffix Array w/ LCP" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/strings%20(14)/Light/SuffixArray%20(14.4).h">$O(N\log N)$</Resource> +</Resources> + +(recommend that you also test against brute force for many small strings) + +## LCP Array + +<Problems problems={problems.lcpSam} /> + +Quickly compute longest common prefix of two suffixes. + +<Problems problems={problems.lcp} /> + +## Inverse Burrows-Wheeler + +<Resources> + <Resource source="GFG" title="Inverting Burrows-Wheeler Transform" url="inverting-burrows-wheeler-transform">could be simpler?</Resource> +</Resources> + +CSES Guide? + +<Problems problems={problems.burSam} /> + +## Run Enumerate + +<Resources> + <Resource source="cp-algo" title="Finding repetitions" url="main_lorentz.html">could be simpler?</Resource> +</Resources> + +(describe how to do easily w/ suffix array) + +<Problems problems={problems.runSam} /> + diff --git a/content/5_Plat/Sweep_Line.mdx b/content/5_Plat/Sweep_Line.mdx new file mode 100644 index 0000000..6a6726e --- /dev/null +++ b/content/5_Plat/Sweep_Line.mdx @@ -0,0 +1,59 @@ +--- +id: sweep-line +title: "Sweep Line" +author: Benjamin Qi +description: Introduction to line sweep. +prerequisites: + - geo-pri +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + closest: [ + new Problem("Kattis", "Closest Pair", "closestpair2", "Normal", false, [], ""), + ], + seg: [ + new Problem("Silver", "Cow Steepchase II", "943", "Normal", false, [], ":|"), + ], + manSam: [ + new Problem("Kattis", "Grid MST", "gridmst", "Intro", false, [], ""), + ], + man: [ + new Problem("CSA", "The Sprawl", "the-sprawl", "Hard", false, [], ""), + ] +}; + +(what's line sweep?) + +<Resources> + <Resource source="CPH" title="30.1, 30.2 - Sweep Line Algorithms"></Resource> + <Resource source="TC" title="Line Sweep Algorithms" url="line-sweep-algorithms"></Resource> +</Resources> + +## Closest Pair + +<Problems problems={problems.closest} /> + +(explanation? KACTL?) + +## Line Segment Intersection + +(refer to previous module) + +<Problems problems={problems.seg} /> + +(filling in the details?) + +## Manhattan MST + +<Problems problems={problems.manSam} /> + +(KACTL code) + +explanation? topcoder prob has + +<Problems problems={problems.man} /> + +TC 760 ComponentsForever
\ No newline at end of file diff --git a/content/7_Advanced/Critical.mdx b/content/6_Advanced/Critical.mdx index dd0759e..dd0759e 100644 --- a/content/7_Advanced/Critical.mdx +++ b/content/6_Advanced/Critical.mdx diff --git a/content/6_Advanced/Extend_Euclid.mdx b/content/6_Advanced/Extend_Euclid.mdx new file mode 100644 index 0000000..51d2c00 --- /dev/null +++ b/content/6_Advanced/Extend_Euclid.mdx @@ -0,0 +1,177 @@ +--- +id: extend-euclid +title: "Extended Euclidean Algorithm" +author: Benjamin Qi +prerequisites: + - intro-nt +description: "?" +frequency: 1 +--- + +import { Problem } from "../models"; + +export const 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={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={problems.crt} /> diff --git a/content/7_Advanced/FFT.mdx b/content/6_Advanced/FFT-ext.mdx index 6fef4d7..b4cf619 100644 --- a/content/7_Advanced/FFT.mdx +++ b/content/6_Advanced/FFT-ext.mdx @@ -1,6 +1,6 @@ --- -id: fft -title: "Fast Fourier Transform and Applications" +id: fft-ext +title: "More Complex Operations Using FFT" author: Benjamin Qi prerequisites: description: "?" @@ -9,54 +9,45 @@ frequency: 0 import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { + ys: [ + new Problem("YS", "Partition Function", "partition_function", "?", false, [], ""), + new Problem("YS", "Bernoulli Number", "bernoulli_number", "?", false, [], ""), + ], general: [ new Problem("Plat", "Tree Depth", "974", "Hard", false, ["genfunc"], ""), new Problem("Plat", "Exercise", "1045", "Hard", false, ["permutation"], "genfuncs not required but possibly helpful"), ], - } }; -<info-block title="Pro Tip"> -Does not appear on USACO. -</info-block> +## More Complex Operations -## FFT +<Resources> + <Resource source="cp-algo" title="Operations on Polynomials & Series" url="algebra/polynomial.html"> </Resource> +</Resources> -### Tutorial +### Implementations - - [cp-algo - FFT](https://cp-algorithms.com/algebra/fft.html) - - [CSA - FFT and Variations](https://csacademy.com/blog/fast-fourier-transform-and-variations-of-it/) - - [CF Tutorial Pt 1](http://codeforces.com/blog/entry/43499) - - [CF Tutorial Pt 2](http://codeforces.com/blog/entry/48798) - - [CF adamant](http://codeforces.com/blog/entry/55572) +<Resources> + <Resource source="Benq" title="Polys" url="https://github.com/bqi343/USACO/tree/master/Implementations/content/numerical/Polynomials"> </Resource> +</Resources> ### Problems - - [K-Inversions](https://open.kattis.com/problems/kinversions) - - [Big Integer](https://dmoj.ca/problem/bts17p8) - - [Matchings](https://open.kattis.com/contests/acpc17open/problems/matchings) - - [Counting Triplets](https://toph.co/p/counting-triplets) - - [Alien Codebreaking](https://open.kattis.com/problems/aliencodebreaking) - - base conversion in $O(N\log^2N)$ - -## More Complex Operations +<Problems problems={problems.ys} /> - - [cp-algo - Operations on Polynomials & Series](https://cp-algorithms.com/algebra/polynomial.html) - - [My Implementations](https://github.com/bqi343/USACO/tree/master/Implementations/content/numerical/Polynomials) - -## yosupo +## Counting -https://judge.yosupo.jp/problem/partition_function -https://judge.yosupo.jp/problem/bernoulli_number +No advanced knowledge about generating functions is required for either of these problems. -## Counting +<Problems problems={problems.general} /> -<problems-list problems={metadata.problems.general} /> +However, these do: - - [zscoder GenFunc Pt 1](https://codeforces.com/blog/entry/77468) - - [zscoder GenFunc Pt 2](https://codeforces.com/blog/entry/77551) +<Resources> + <Resource source="CF" title="zscoder - GenFunc Pt 1" url="https://codeforces.com/blog/entry/77468"> </Resource> + <Resource source="CF" title="zscoder - GenFunc Pt 2" url="https://codeforces.com/blog/entry/77551"> </Resource> +</Resources> ## [F2: Slime & Sequences](https://codeforces.com/contest/1349/problem/F2) @@ -66,7 +57,7 @@ Of course, this will be split into several parts to make it easier to follow. ### Part 0: Includes -<warning-block>400+ lines of polynomial template</warning-block> +<Warning>400+ lines of polynomial template</Warning> (link?) @@ -87,7 +78,7 @@ $$ We can naively implement this as follows. This gets MLE on F1, but at least it gives the correct results. -<spoiler title="Slow Solution"> +<Spoiler title="Slow Solution"> ```cpp int n; @@ -124,7 +115,7 @@ int main() { ``` -</spoiler> +</Spoiler> ### Part 2: Setting up a function to compute the same thing more quickly diff --git a/content/6_Advanced/FFT.mdx b/content/6_Advanced/FFT.mdx new file mode 100644 index 0000000..a1b86d3 --- /dev/null +++ b/content/6_Advanced/FFT.mdx @@ -0,0 +1,27 @@ +--- +id: fft +title: "Introduction to Fast Fourier Transform" +author: Benjamin Qi +prerequisites: +description: "?" +frequency: 0 +--- + +## FFT + +### Tutorial + + - [cp-algo - FFT](https://cp-algorithms.com/algebra/fft.html) + - [CSA - FFT and Variations](https://csacademy.com/blog/fast-fourier-transform-and-variations-of-it/) + - [CF Tutorial Pt 1](http://codeforces.com/blog/entry/43499) + - [CF Tutorial Pt 2](http://codeforces.com/blog/entry/48798) + - [CF adamant](http://codeforces.com/blog/entry/55572) + +### Problems + + - [K-Inversions](https://open.kattis.com/problems/kinversions) + - [Big Integer](https://dmoj.ca/problem/bts17p8) + - [Matchings](https://open.kattis.com/contests/acpc17open/problems/matchings) + - [Counting Triplets](https://toph.co/p/counting-triplets) + - [Alien Codebreaking](https://open.kattis.com/problems/aliencodebreaking) + - base conversion in $O(N\log^2N)$
\ No newline at end of file diff --git a/content/6_Advanced/Flow_LB.mdx b/content/6_Advanced/Flow_LB.mdx new file mode 100644 index 0000000..419b3c1 --- /dev/null +++ b/content/6_Advanced/Flow_LB.mdx @@ -0,0 +1,35 @@ +--- +id: flow-lb +title: "Flow with Lower Bounds" +author: Benjamin Qi +prerequisites: + - Platinum - Flows +description: "?" +frequency: 0 +--- + +import { Problem } from "../models"; + +export const problems = { + hungry: [ + new Problem("DMOJ", "Hungry Squirrels", "wac4p6", "Normal", false, [], ""), + ], + lower: [ + new Problem("CF", "Diverse Singing", "https://codeforces.com/group/ZFgXbZSjvp/contest/274398/problem/C", "Normal", false, [], ""), + new Problem("CF", "Captain America", "contest/704/problem/D", "Normal", false, [], ""), + new Problem("CF", "Incorrect Flow", "contest/708/problem/D", "Normal", false, [], ""), + new Problem("AC", "Multi-Path Story", "https://atcoder.jp/contests/jag2013summer-day4/tasks/icpc2013summer_day4_i", "Normal", false, [], ""), + ], +}; + +<Problems problems={problems.hungry} /> + +### Tutorial + +<Resources> + <Resource source="CMU" title="Flow Extensions" url="https://www.cs.cmu.edu/~ckingsf/bioinfo-lectures/flowext.pdf"></Resource> +</Resources> + +### Problems + +<Problems problems={problems.lower} />
\ No newline at end of file diff --git a/content/7_Advanced/Game_Theory.mdx b/content/6_Advanced/Game_Theory.mdx index ca6a069..1b52ea7 100644 --- a/content/7_Advanced/Game_Theory.mdx +++ b/content/6_Advanced/Game_Theory.mdx @@ -6,9 +6,9 @@ description: "?" frequency: 0 --- -<info-block title="Pro Tip"> +<Info title="Pro Tip"> Does not appear on USACO. -</info-block> +</Info> * Tutorial * [HackerRank](https://www.hackerrank.com/topics/game-theory-and-grundy-numbers) diff --git a/content/7_Advanced/LCT.mdx b/content/6_Advanced/LCT.mdx index 0e59fe0..3058bfa 100644 --- a/content/7_Advanced/LCT.mdx +++ b/content/6_Advanced/LCT.mdx @@ -10,8 +10,7 @@ frequency: 1 import { Problem } from "../models"; -export const metadata = { - problems: { +export const problems = { lctSam: [ new Problem("YS", "Vertex Add Path Sum", "dynamic_tree_vertex_add_path_sum", "Normal", false, ["LCT"], ""), ], @@ -22,11 +21,12 @@ export const metadata = { new Problem("SPOJ", "Dynamic LCA", "DYNALCA", "Normal", false, ["LCT"], ""), new Problem("DMOJ", "Squirrel Cities", "wac4p7", "Normal", false, ["LCT"], "link, cut, min edge along path"), new Problem("HR", "Balanced Tokens", "https://www.hackerrank.com/contests/pwshpc-online-round/challenges/pwsh-tokens/problem", "Normal", false, ["LCT"], ""), - new Problem("CSES", "Treasure Hunt (CEOI)", "https://cses.fi/174/list/", "Normal", false, ["LCT"], ""), + new Problem("CSES", "CEOI - Treasure Hunt", "https://cses.fi/174/list/", "Normal", false, ["LCT"], ""), new Problem("DMOJ", "Dynamic Tree Test (Easy)", "ds5easy", "Hard", false, ["LCT"], ""), new Problem("CF", "Train Tracking", "contest/1344/problem/E", "Hard", false, ["LCT"], "* switching can be done with LCT access, set min on path with lazy prop * [Solution](https://codeforces.com/contest/1344/submission/79212522)"), new Problem("CF", "Tree or not Tree", "contest/117/problem/E", "Hard", false, ["LCT"], "tree + one edge"), new Problem("CF", "CERC 17 D", "gym/101620", "Hard", false, ["LCT"], "LCT not needed, but can directly apply solution from The Applicant"), + new Problem("ojuz", "IOI - Dancing Elephants", "IOI11_elephants", "Hard", false, [], ""), ], subSam: [ new Problem("YS", "Vertex Add Subtree Sum", "dynamic_tree_vertex_add_subtree_sum", "Normal", false, ["LCT"], ""), @@ -37,7 +37,6 @@ export const metadata = { new Problem("CF", "Old Driver Tree", "contest/1172/problem/E", "Hard", false, ["LCT"], ""), new Problem("DMOJ", "Dynamic Tree Test", "ds5", "Very Hard", false, ["LCT"], ""), ] - } }; ## Splay Tree @@ -49,21 +48,24 @@ export const metadata = { (Implementation?) + - [Euler Tour Tree](https://codeforces.com/blog/entry/18369) + ## Link Cut Tree - Paths -<problems-list problems={metadata.problems.lctSam} /> +<Problems problems={problems.lctSam} /> ### Tutorial - [MIT](http://courses.csail.mit.edu/6.851/spring12/scribe/L19.pdf) - [CTSC 2014 Translation](https://www.overleaf.com/read/zbbwngftkrkx?fbclid=IwAR1VJmGscfSZJHejVqU5_2Nlm8-R09pFlU7PpEg_i3CXEd0Qw-dzliSn5RI) - -(Implementation?) +### Implementation ### Problems -<problems-list problems={metadata.problems.lct} /> +<Problems problems={problems.lct} /> + +<!-- ### USACO Camp @@ -76,9 +78,11 @@ export const metadata = { * HLD + Treap * or two LCTs, one for real tree labels and one for query labels + --> + ## Link Cut Tree - Subtrees -<problems-list problems={metadata.problems.subSam} /> +<Problems problems={problems.subSam} /> ### Tutorial @@ -86,4 +90,4 @@ export const metadata = { ### Problems -<problems-list problems={metadata.problems.sub} /> +<Problems problems={problems.sub} /> diff --git a/content/7_Advanced/Matroid_Isect.mdx b/content/6_Advanced/Matroid_Isect.mdx index 7f65a9e..7f65a9e 100644 --- a/content/7_Advanced/Matroid_Isect.mdx +++ b/content/6_Advanced/Matroid_Isect.mdx diff --git a/content/7_Advanced/MinCostFlow.mdx b/content/6_Advanced/MinCostFlow.mdx index bd5a4e7..e3522b5 100644 --- a/content/7_Advanced/MinCostFlow.mdx +++ b/content/6_Advanced/MinCostFlow.mdx @@ -8,10 +8,6 @@ prerequisites: frequency: 1 --- -<info-block title="Pro Tip"> -Historically restricted to USACO Camp. -</info-block> - (Hungarian) * Tutorial diff --git a/content/7_Advanced/Multiplicative.mdx b/content/6_Advanced/Multiplicative.mdx index 948a1e9..948a1e9 100644 --- a/content/7_Advanced/Multiplicative.mdx +++ b/content/6_Advanced/Multiplicative.mdx diff --git a/content/7_Advanced/Persistent.mdx b/content/6_Advanced/Persistent.mdx index df1c07a..1aecca9 100644 --- a/content/7_Advanced/Persistent.mdx +++ b/content/6_Advanced/Persistent.mdx @@ -8,10 +8,6 @@ prerequisites: frequency: 1 --- -<info-block title="Pro Tip"> -Historically restricted to USACO Camp. -</info-block> - ## Persistent Heap (Leftist) diff --git a/content/6_Advanced/Segtree_Beats.mdx b/content/6_Advanced/Segtree_Beats.mdx new file mode 100644 index 0000000..ab815c0 --- /dev/null +++ b/content/6_Advanced/Segtree_Beats.mdx @@ -0,0 +1,29 @@ +--- +id: segtree-beats +title: "Segment Tree Beats" +author: Benjamin Qi +prerequisites: + - Platinum - Range Update Range Query +description: "?" +frequency: 1 +--- + +import { Problem } from "../models"; + +export const problems = { + segTreeBeats: [ + new Problem("YS", "Range Chmin Chmax Add Range Sum", "range_chmin_chmax_add_range_sum", "Hard", false, ["SegTreeBeats"], ""), + ], +}; + +### Tutorial + +<Resources> + <Resource source="CF" title="Intro to Segment Tree Beats" url="blog/entry/57319"></Resource> +</Resources> + +### Problems + +<Problems problems={problems.segTreeBeats} /> + +(300iq insane problem??) diff --git a/content/6_Advanced/String_Suffix.mdx b/content/6_Advanced/String_Suffix.mdx new file mode 100644 index 0000000..fe73532 --- /dev/null +++ b/content/6_Advanced/String_Suffix.mdx @@ -0,0 +1,387 @@ +--- +id: string-suffix +title: "String Suffix Structures" +author: Benjamin Qi, Siyong Huang +description: "Suffix Automata, Suffix Trees, and (TBD) Palindromic Trees" +prerequisites: + - string-search +frequency: 0 +--- + +export const problems = { + auto: [ + new Problem("Plat", "Standing Out from the Herd", "768", "Hard", false, [], ""), + ], + tree: [ + new Problem("CF", "Security", "contest/1037/problem/H", "Hard", false, ["Suffix Tree"], ""), + ] +}; + +## String Suffix Structures + + * https://www.spoj.com/problems/SUBLEX/ + * https://open.kattis.com/problems/stringmultimatching + * https://codeforces.com/gym/102129/problem/I + * https://codeforces.com/contest/235/problem/C + +A lot of problems can be solved with Suffix Arrays, Suffix Automata, or Suffix Trees. The solution may just be slightly easier/harder with the various data structures. + +### Suffix Automaton + +The **Suffix Automaton** is a directed acyclic word graph (DAWG), such that each path in the graph traces out a distinct substring of the original string. + +<Resources> + <Resource source="CF" title="A short guide to suffix automata" url="blog/entry/20861">Explanation of Suffix Automata</Resource> + <Resource source="cp-algo" title="Suffix Automaton" url="string/suffix-automaton.html" starred>Excellent Suffix Automaton tutorial</Resource> + <Resource source="CF" title="adamant - history of recurring problem" url="blog/entry/62331">More problems!</Resource> +</Resources> + +#### Implementation + +<Resources> + <Resource source="Benq" title="Suffix Automaton" url="https://github.com/bqi343/USACO/blob/master/Implementations/content/strings%20(14)/Heavy/SuffixAutomaton.h"> </Resource> +</Resources> + +### Suffix Tree + +The **Suffix Tree** is a trie that contains all suffixes of a string. Naively, this would take up $O(N^2)$ memory, but *path compression* enables it to be represented and computed in linear memory. + +<Resources> + <Resource source="CF" title="Suffix Tree. Ukkonen's algorithm" url="blog/entry/16780">Explanation of Ukkonen's Suffix Tree Algorithm</Resource> +</Resources> + +#### Implementation + +<Resources> + <Resource source="cp-algo" title="Suffix Tree. Ukkonen's Algorithm" url="string/suffix-tree-ukkonen.html">Implementation of Ukkonen's Algorithm</Resource> +</Resources> + +### Generate Suffix Array from Suffix Tree + +A suffix array can be generated by the suffix tree by taking the dfs traversal of the suffix tree. + +<Spoiler title="Sample Code: Suffix Array from Suffix Tree"> + +<LanguageSection> + +<CPPSection> + +<!-- https://codeforces.com/edu/course/2/lesson/2/2/practice/contest/269103/submission/85759835 --> + +```cpp +int N, sa[MN], ctr;//length of string, suffix array, counter + +struct Edge +{ +public: + int n, l, r;//node, edge covers s[l..r] + explicit operator bool() const {return n!=-1;} +} c[MN*2][26]; // edges of a suffix tree + +void dfs(int n=0, int d=0) +{ + bool c=0;// Has child. If false, then this node is a leaf + for(int i=0;i<26;++i) + if(c[n][i]) + { + c=1; + dfs(c[n][i].n, d+c[n][i].r-c[n][i].l); + } + if(!c) + sa[ctr++]=N-d; +} +``` +</CPPSection> + +</LanguageSection> + +</Spoiler> + +### Generate Suffix Tree from Suffix Array + +Of course, the above operation can be reversed as well. +Each element in the suffix array corresponds to a leaf in the suffix tree. +The LCP array stores information about the Lowest Common Ancestor of two adjacent elements in the suffix array. +Using these two pieces of information, we can construct the suffix tree from the suffix array in linear time. + +<Info title="Pro Tip!"> + +Frequently, string suffix structures are greatly simplified by adding a 'terminator' character, such as `$` or `-`, to the end of the string. +In the following samples, these terminators will be explicitly added. + +</Info> + +<Spoiler title="Sample Code: Suffix Tree from Suffix Array"> + +<LanguageSection> + +<CPPSection> + +```cpp +//lightly tested + +int N; +char s[MN]; +int sa[MN]; //suffix array +int lcp[MN]; //lcp[i] stores the longest common prefix between s[sa[i-1]..] and s[sa[i]..] + +Edge c[MN*2][MK]; // edges of suffix tree +int d[MN*2];//length of string corresponding to a node in the suffix tree +int q[MN*2], Q, ctr, rm[MN];//q is used as stack. ctr counts number of nodes in tree +std::stack<int> ins[MN]; +void build_tree() +{ + q[0]=N; Q=0; + for(int i=N-1;i>=1;--i) + { + while(Q&&lcp[q[Q]]>lcp[i])--Q; + if(lcp[q[Q]]!=lcp[i]) ++rm[q[Q]];//Right bound of the range where lcp is the longest common prefix + q[++Q]=i; + } + q[0]=0, Q=0; + for(int i=1;i<N;++i) + { + while(Q&&lcp[q[Q]]>lcp[i])--Q; + if(lcp[q[Q]]!=lcp[i]) ins[q[Q]].push(i);//Left bound of the range where lcp first becomes a longest common prefix + q[++Q]=i; + } + //The left and right bounds computed above can be interpreted as the dfs preorder and postorder + q[0]=0, Q=0;//This q array now holds the stack of ancestors for every new node created + auto nn=[&](int l, int dd) + { + ++ctr; + d[ctr]=dd; + int p=q[Q]; + // p is the parent of this node, per definition of stack q + int r=l+dd; + //s[l..r] is the string corresponding to the node that we are inserting + l+=d[p]; + //d[p] is the length of the parent, so s[l..l+d[p]] would have already been covered by the node's ancestors + c[p][s[l]]={ctr,l,r}; + return ctr; + }; + for(int i=0;i<N;++i) + { + Q-=rm[i]; + for(int x;!ins[i].empty();ins[i].pop()) + { + x=ins[i].top(); + x=nn(sa[x], lcp[x]); + // sa[x+1] would be equivalent, per definition of lcp + q[++Q]=x; + } + nn(sa[i], N-sa[i]); + } +} +``` + +</CPPSection> + +</LanguageSection> + +</Spoiler> + +### Generate Suffix Tree from Suffix Automaton + +One interesting thing about Suffix Trees and Suffix Automata is that the link tree of a Suffix Automaton is equivalent to the Suffix Tree of the reversed string. +Since Suffix Automata are much easier to create than Suffix Trees, we can use this as an alternate method to build a Suffix Tree, all in linear time too! + +<Spoiler title="Sample Code: Suffix Tree from Suffix Automaton"> + +<LanguageSection> + +<CPPSection> + +<!-- https://codeforces.com/edu/course/2/lesson/2/2/practice/contest/269103/submission/85759835 - This submission contains both --> + +```cpp +char s[MN]; //string +int ord[MN]; // nodes representing prefixes of the string s +int u[MN*2]; // whether the node has already been created +int l[MN*2]; // link in suffix automaton +Edge c[MN*2][27]; // edge of suffix tree (not automaton; structure of automaton is not necessary to build stree) +void build_tree() +{ + s[N] = 26; // terminator + for(int i=N;i>=0;--i) ord[i]=append(ord[i+1], s[i]); + for(int i=0,x,r,l;i<=N;++i) + { + x=ord[i], r=N+1; + for(;x&&!u[x];x=l[x]) + { + l=r-d[x]+d[l[x]]; + c[l[x]][s[l]]={x, l, r}; + r=l; + u[x]=1; + } + } +} +``` +</CPPSection> + +</LanguageSection> + +</Spoiler> + +### Example: Standing Out + +<Problems problems={problems.auto} /> + +<Spoiler title="With Suffix Automaton"> + +<!-- Checked via USACO Practice --> + +```cpp +#include <cstdio> +#include <cstring> +#include <vector> + +FILE * IN, * OUT; +typedef long long ll; +const int MN = 1e5+10, MM = MN*2; +char s[MN]; +std::vector<int> down[MM]; +int N, v[MM], c[MM][26], l[MM], d[MM], topo[MM], T, X; +ll f[MN], cnt[MM]; +bool u[MM]; + +/* +Key Variables: + +s: input strings +down: link tree of automaton +v: information regarding which cow each node belongs to +c: child array of automaton +l: link (of automaton) +d: depth (of automaton) +topo: toposort (of automaton) +T, X: counters for toposort and automaton +f: answer +cnt: number of ways to reach a node from the root +u: visited array for toposort +*/ + +//add cow b to value a +//value = -1: no cow assigned +//value = -2: multiple cows assigned +//value = 0..N: cow id +void merge(int& a, int b) +{ + if(!~a) a=b; + else if(~b&&a!=b) a=-2; +} + +//template automaton code +int append(int p, char x) +{ + if(~c[p][x]) + { + int q=c[p][x]; + if(d[q]==d[p]+1) + return q; + else + { + ++X; + for(int i=0;i<26;++i) c[X][i]=c[q][i]; + l[X]=l[q], d[X]=d[p]+1; + l[q]=X; + for(;~p&&c[p][x]==q;p=l[p]) + c[p][x]=l[q]; + return l[q]; + } + } + int n = ++X; + d[n]=d[p]+1; + for(;~p&&!~c[p][x];p=l[p]) + c[p][x]=n; + if(!~p) + l[n]=0; + else + { + int q=c[p][x]; + if(d[q]==d[p]+1) + l[n]=q; + else + { + ++X; + for(int i=0;i<26;++i) c[X][i]=c[q][i]; + l[X]=l[q], d[X]=d[p]+1; + l[n]=l[q]=X; + for(;~p&&c[p][x]==q;p=l[p]) + c[p][x]=l[q]; + } + } + return n; +} + +//DFS along links +void dfs2(int n=0) +{ + for(int x:down[n]) + { + dfs2(x); + merge(v[n], v[x]); + } +} +//DFS along suffix automaton. This builds the toposort +void dfs(int n=0) +{ + u[n]=1; + for(int i=0;i<26;++i) + { + int y=c[n][i]; + if(~y && !u[y]) dfs(y); + } + topo[T++] = n; +} + +int main(void) +{ + IN = fopen("standingout.in", "r"), OUT = fopen("standingout.out", "w"); + memset(v, -1, sizeof v); + memset(c, -1, sizeof c); + fscanf(IN, "%d", &N); + d[0]=0, l[0]=-1; + for(int i=0;i<N;++i) + { + fscanf(IN, " %s", s); + int n=0; + for(int j=0;s[j];++j) + { + n = append(n, s[j]-'a'); //build automaton + merge(v[n], i); + } + } + //build link tree + for(int i=1;i<=X;++i) + down[l[i]].push_back(i); + dfs();//dfs link tree + dfs2();//dfs automaton + cnt[0]=1; + for(int i=T-1, x;i>=0;--i) + { + x=topo[i]; + for(int j=0;j<26;++j) + if(~c[x][j]) + cnt[c[x][j]]+=cnt[x];//count number of paths from root to a node + if(v[x]>=0) + f[v[x]]+=cnt[x];//if this node is associated with a unique cow, add to answer + } + for(int i=0;i<N;++i) + fprintf(OUT, "%lld\n", f[i]); + return 0; +} +``` + +</Spoiler> + +### Problems + +<Problems problems={problems.tree} /> + +## Extending Palindromic Tree + + * [Palindrome Partition (Very Hard)](https://codeforces.com/contest/932/problem/G) + * [Partial Solution](https://codeforces.com/blog/entry/19193) + * [Palindromic Magic (Insane)](https://codeforces.com/contest/1081/problem/H) + diff --git a/content/6_Advanced/Treaps.mdx b/content/6_Advanced/Treaps.mdx new file mode 100644 index 0000000..8249745 --- /dev/null +++ b/content/6_Advanced/Treaps.mdx @@ -0,0 +1,117 @@ +--- +id: treaps +title: "Treaps" +author: Benjamin Qi +description: "?" +prerequisites: + - Platinum - Range Update Range Query +frequency: 2 +--- + +import { Problem } from "../models"; + +export const problems = { + treeRot: [ + new Problem("POI", "Tree Rotations 2", "https://szkopul.edu.pl/problemset/problem/b0BM0al2crQBt6zovEtJfOc6/site/?key=statement", "Normal", false, [], ""), + ] +}; + +## Merging + +## Splitting + +## Merging (Arbitrary Keys) + +<Problems problems={problems.treeRot} /> + + +<Spoiler title="Tree Rotations 2 Solution"> + +Apparently [this paper](https://www.cs.cmu.edu/~scandal/papers/treaps-spaa98.pdf) proves that this runs in $O(n\log n)$. + +```cpp +int n; +ll ans, inv; + +typedef struct tnode* pt; +struct tnode { + int pri, val; pt c[2]; // essential + int sz; // for range queries + tnode (int _val) { + pri = rng(); val = _val; + sz = 1; c[0] = c[1] = NULL; + } +}; +int getsz(pt x) { return x?x->sz:0; } +pt calc(pt x) { + x->sz = 1+getsz(x->c[0])+getsz(x->c[1]); + return x; +} +pair<pt,pt> split(pt t, int v) { // >= v goes to the right + if (!t) return {t,t}; + if (t->val >= v) { + auto p = split(t->c[0], v); t->c[0] = p.s; + return {p.f,calc(t)}; + } else { + auto p = split(t->c[1], v); t->c[1] = p.f; + return {calc(t),p.s}; + } +} + +pt merge(pt a, pt b) { + if (!a || !b) return a?:b; + if (a->pri > b->pri) { + auto B = split(b,a->val); + inv += (ll)(1+getsz(a->c[0]))*getsz(B.s); + a->c[0] = merge(a->c[0],B.f); a->c[1] = merge(a->c[1],B.s); + return calc(a); + } else { + auto A = split(a,b->val); + inv += (ll)getsz(A.f)*(1+getsz(b->c[1])); + b->c[0] = merge(A.f,b->c[0]); b->c[1] = merge(A.s,b->c[1]); + return calc(b); + } +} + +pt go() { + int x; re(x); + if (x) return new tnode(x); + pt L = go(); pt R = go(); + ll tot = (ll)L->sz*R->sz; inv = 0; + pt cur = merge(L,R); assert(inv <= tot); // dbg("HUH",tot,inv); + ans += min(inv,tot-inv); + return cur; +} + +int main() { + setIO(); re(n); go(); + ps(ans); +} +``` + +</Spoiler> + +<Optional> + +[This](https://codeforces.com/blog/entry/67980) CF post describes a more general version of the problem above. + +</Optional> + +## Tutorial + + - [Quora](https://threads-iiith.quora.com/Treaps-One-Tree-to-Rule-em-all-Part-1) + - [PPT](https://docs.google.com/presentation/d/14xgtdDWnIBwmJRAuIdZ8FvLZcX9uRxnNoGOGAQRDIvc/edit?usp=sharing) + - Samuel Hsiang Guide (see resources) + +## Problems + + - [Old Gold - Airplane Boarding](http://www.usaco.org/index.php?page=viewproblem2&cpid=402) + - [Strings](https://csacademy.com/contest/archive/task/strings/) [](181) + - [Points & Distances](https://www.hackerearth.com/problem/algorithm/septembereasy-points-and-distances-d30d0e6b/description/) [](185) + +IOI 2013 Game - https://oj.uz/submission/242393 + +A2OJ: https://a2oj.com/category?ID=14 + + + ?
\ No newline at end of file diff --git a/content/6_Plat/2DRQ.mdx b/content/6_Plat/2DRQ.mdx deleted file mode 100644 index 9b2655c..0000000 --- a/content/6_Plat/2DRQ.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -id: 2DRQ -title: "2D Range Queries" -author: Benjamin Qi -prerequisites: - - Platinum - Range Update Range Query -description: "Extending Range Queries to 2D (and beyond)." -frequency: 1 ---- - -See [my implementations](https://github.com/bqi343/USACO/tree/master/Implementations/content/data-structures/2D%20Range%20Queries%20(15.2)). - -## Static Array Queries - - - [Multi-Dimensional RMQ (retrograd)](https://codeforces.com/blog/entry/53810) - - GP of Serbia 2020 B - -## 2D BIT - -### Tutorials - - - [GFG 2D BIT](https://www.geeksforgeeks.org/two-dimensional-binary-indexed-tree-or-fenwick-tree/) - - [TopCoder BIT](https://www.topcoder.com/community/competitive-programming/tutorials/binary-indexed-trees/) - - Suppose that you want to update $N\approx 10^5$ points with both coordinates in the range $[1,N]$. - - The 2D BITs mentioned above use $O(N^2)$ memory, which is too much. - - Can be reduced to $O(N\log^2N)$ memory with unordered map, but this might also be too much (and too slow). - - If you know the points to be updated beforehand then you can reduce to $O(N\log N)$ memory (and no maps). - - [my 1D offline BIT](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BIToff.h) - - [my 2D offline BIT](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/2D%20Range%20Queries%20(15.2)/BIT2DOff%20(15.2).h) - -<optional-content title="Range Update and Range Query in Higher Dimensions"> - -You can extend the 1D BIT solution for range update range query to higher dimensions as well - - - [Paper](https://arxiv.org/pdf/1311.6093.pdf) - - USACO Camp - "Cows Play Global Thermonuclear War" (2D case) - -</optional-content> - -### Problems - - - [CSES Forest Queries II](https://cses.fi/problemset/task/1739) - - [thecodingwizard's implementation](https://github.com/thecodingwizard/competitive-programming/blob/master/cses/Forest%20Queries%20II.cpp) - - [DMOJ Soriya's Programming Project](https://dmoj.ca/problem/dmopc19c7p5) - - intended complexity is $O(N\log^2 N)$ - - compressed 2D BIT - - rather difficult to pass within time and memory limits - - Make sure to use `\n` instead of `endl`! - - [thecodingwizard's implementation with Benq's 2d offline bit](https://github.com/thecodingwizard/competitive-programming/blob/master/DMOJ/Soriyas%20Programming%20Project.cpp) - - or do divide & conquer with a 1D BIT - - same as first problem [here](https://robert1003.github.io/2020/01/31/cdq-divide-and-conquer.html) - - [thecodingwizard's (messy) implementation](https://github.com/thecodingwizard/competitive-programming/blob/master/DMOJ/Soriya%20Programming%20Project%201d%20BIT%20cdq%20dnc.cpp) based off of article above - - [IOI 2007 Pairs](https://wcipeg.com/problem/ioi0722) - - [DMOJ Crowded Cities](https://dmoj.ca/problem/bfs17p6) - - [USACO Plat Friendcross](http://www.usaco.org/index.php?page=viewproblem2&cpid=722) - - [USACO Plat Mowing](http://www.usaco.org/index.php?page=viewproblem2&cpid=601) - -## 2D Segment Tree - -Note: no lazy propagation in 2D. - -### Short Description - - - CSES 28.2 (Sparse Segment Tree), 28.4 - - Segment Tree (or BIT) nested inside segment tree - - use 2D offline BIT instead whenever possible (faster, lower memory) - -### Problems - - - [POI Tetris 3D](https://szkopul.edu.pl/problemset/problem/OQjANSOOD_-c38gh8p6g3Gxp/site/?key=statement) - - [IOI 2013 Game](http://wcipeg.com/problem/ioi1323) - - good implementation?
\ No newline at end of file diff --git a/content/6_Plat/Aliens.mdx b/content/6_Plat/Aliens.mdx deleted file mode 100644 index 910a010..0000000 --- a/content/6_Plat/Aliens.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -id: aliens -title: "Aliens Trick" -author: Benjamin Qi -description: "?" -prerequisites: - - Plat - Convex Hull ---- - -IOI 2016 Aliens
\ No newline at end of file diff --git a/content/6_Plat/Centroid.mdx b/content/6_Plat/Centroid.mdx deleted file mode 100644 index c5a6d8c..0000000 --- a/content/6_Plat/Centroid.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -id: centroid -title: "Centroid Decomposition" -author: Benjamin Qi -prerequisites: - - Silver - Depth First Search -description: "?" -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("CF", "Ciel the Commander", "problemset/problem/321/C", "Easy", false, ["Centroid"]), - new Problem("YS", "Frequency Table of Tree Distance", "frequency_table_of_tree_distance", "Hard", false, ["Centroid", "FFT"], ""), - new Problem("DMOJ", "Bob Equilibrium", "dmopc19c7p6", "Hard", false, ["Centroid"], "tight time limit"), - new Problem("Plat", "At Large", "793", "Very Hard", false, ["Centroid"], "tight time limit"), - ] - } -}; - -(another DMOJ prob?) - -### Tutorial - - - [GeeksForGeeks](http://www.geeksforgeeks.org/centroid-decomposition-of-tree/) - - [Carpanese](https://medium.com/carpanese/an-illustrated-introduction-to-centroid-decomposition-8c1989d53308) - -### Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/6_Plat/DP_Bitmasks.mdx b/content/6_Plat/DP_Bitmasks.mdx deleted file mode 100644 index a16247d..0000000 --- a/content/6_Plat/DP_Bitmasks.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: dp-bitmasks -title: "Dynamic Programming on Bitmasks" -author: Michael Cao -prerequisites: - - Bit Operations - - Gold - Introduction to Dynamic Programming -description: DP problems that require iterating over subsets. -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("CSES", "Hamiltonian Flights", "1690", "Easy", false, ["Bitmasks"], ""), - new Problem("CSES", "Elevator Rides", "1653", "Normal", false, ["Bitmasks"], ""), - new Problem("ojuz", "IZhO Bank", "IZhO14_bank", "Normal", false, ["Bitmasks"], ""), - new Problem("YS", "Max Indep Set", "maximum_independent_set", "Hard", false, ["Bitmasks", "Meet in Middle"], ""), - ], - } -}; - -Has not been the solution to any platinum problem, but appeared in old gold and can be used for subtasks. - -## Tutorial - - - [PAPS 9.4](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - CPH 10 (Bit manipulation), 19.2 (Hamiltonian Paths) - - [Dynamic Programming Over Subsets (Codeforces)](https://codeforces.com/blog/entry/337) - - [Dynamic Programming and Bit Masking (HackerEarth)](https://www.hackerearth.com/practice/algorithms/dynamic-programming/bit-masking/tutorial/) - -## Problems - -<problems-list problems={metadata.problems.general} /> - - - [Old Gold - Moovie Mooving](http://www.usaco.org/index.php?page=viewproblem2&cpid=515) - - [AC Matching](https://atcoder.jp/contests/dp/tasks/dp_o) - - [CF Square Subsets](https://codeforces.com/contest/895/problem/C) - - [CF Guards in the Storehouse](https://codeforces.com/problemset/problem/845/F) - - [Kattis Cat & Mice](https://open.kattis.com/problems/catandmice) [](66) - - plus a bit of geometry
\ No newline at end of file diff --git a/content/6_Plat/Eulerian_Tours.mdx b/content/6_Plat/Eulerian_Tours.mdx deleted file mode 100644 index 070316c..0000000 --- a/content/6_Plat/Eulerian_Tours.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -id: eulerian-tours -title: "Eulerian Tours" -author: Benjamin Qi -prerequisites: - - Silver - Depth First Search -description: Visiting all edges of a graph exactly once. -frequency: 0 ---- - -Has not appeared on a recent USACO contest. - -### Standard - - - [Mail Delivery](https://cses.fi/problemset/task/1691) - - Undirected Euler Tour - - [Teleporters](https://cses.fi/problemset/task/1693) - - Directed Euler Tour - -### Tutorial - - - CPH (19, Path & Circuits) - -### Problems - - - [Matching Substrings](https://csacademy.com/contest/archive/task/matching-substrings/) [](87) diff --git a/content/6_Plat/Flows.mdx b/content/6_Plat/Flows.mdx deleted file mode 100644 index 5a549c6..0000000 --- a/content/6_Plat/Flows.mdx +++ /dev/null @@ -1,57 +0,0 @@ ---- -id: max-flow -title: "Maximum Flow" -author: Benjamin Qi -prerequisites: - - Gold - Breadth First Search -description: Introduces maximum flow as well as flow with lower bounds. -frequency: 1 ---- - -Has not been the solution to any platinum problem, but appeared in old gold. - -## Maximum Flow - - - [CSES Download Speed](https://cses.fi/problemset/task/1694) - -### Tutorial - - - [CPC.10](https://github.com/SuprDewd/T-414-AFLV/tree/master/10_graphs_3_network_flow) - - CPH (20, Flows & Cuts) - - Topics - - Bipartite Matching? - - Edmonds-Karp Algorithm - - Min-Cut Max-Flow Theorem - - [Closure Problem](https://en.wikipedia.org/wiki/Closure_problem) - -### Problems - - - CSES - - [School Dance](https://cses.fi/problemset/task/1696) - - [Coin Grid](https://cses.fi/problemset/task/1709) - - [Distinct Routes](https://cses.fi/problemset/task/1711) - - Other - - [Goods Transportation](http://codeforces.com/problemset/problem/724/E) [](52) - - [ARC 85 MUL](http://arc085.contest.atcoder.jp/tasks/arc085_c) [](67) - - [Fashion](https://csacademy.com/contest/rmi-2017-day-1/task/fashion/statement/) [](95) - - [Fast Flow](http://www.spoj.com/problems/FASTFLOW/) [](107) - - [Card Game](http://codeforces.com/problemset/problem/808/F) [](135) - -## Flow w/ Lower Bounds - - - Tutorial - - [??](https://pdfs.semanticscholar.org/03a2/785783f43202925da70ae842eeda9cebd77e.pdf) - - [CMU](https://www.cs.cmu.edu/~ckingsf/bioinfo-lectures/flowext.pdf) - - Problems - - [Diverse Singing](https://codeforces.com/group/ZFgXbZSjvp/contest/274398/problem/C) - - [Captain America](https://codeforces.com/contest/704/problem/D) - - [Incorrect Flow](https://codeforces.com/contest/708/problem/D) - - [Multi-Path Story](https://atcoder.jp/contests/jag2013summer-day4/tasks/icpc2013summer_day4_i) - - [WAC 4 - Hungry Squirrels](https://dmoj.ca/problem/wac4p6) - - - * Faster Max Flow - * Hopcroft-Karp Bipartite Matching - * Dinic's Algorithm - * Push-Relabel - * [Fast Flow Implementation - Chilli](https://codeforces.com/blog/entry/66006)
\ No newline at end of file diff --git a/content/6_Plat/Geo_Pri.mdx b/content/6_Plat/Geo_Pri.mdx deleted file mode 100644 index 18a5db6..0000000 --- a/content/6_Plat/Geo_Pri.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: geo-pri -title: "Geometry Primitives" -author: Benjamin Qi -description: Basic setup for geometry problems and introduction to line sweep. ---- - -## Primitives - -You should know basic operations like cross product and dot product. For platinum specifically, you should be fine as long as you know how to code **convex hull**. - -### Tutorial - - - [CPC.12](https://github.com/SuprDewd/T-414-AFLV/tree/master/12_geometry) - - basic geometry - - convex hulls - - polygon area - - point in polygon - - CPH 29 (Geometry), 30.1, 30.2 (Sweep line algorithms) - - [TopCoder - Basic Geometry Concepts](https://www.topcoder.com/community/competitive-programming/tutorials/geometry-concepts-basic-concepts/) - - [CF - Point Class](https://codeforces.com/blog/entry/48122) - - [C++ - std::complex](https://codeforces.com/blog/entry/22175) - - [cp-algo - Geometry: "Elementary Operations"](https://cp-algorithms.com/) - - [vlecomte - Geometry Handbook](https://codeforces.com/blog/entry/59129) - - [My Templates](https://github.com/bqi343/USACO/tree/master/Implementations/content/geometry%20(13)/Primitives) - -### Problems - - - Template Testing - - [yosupo: Sort Points by Arg](https://judge.yosupo.jp/problem/sort_points_by_argument) - - [Kattis Segment Distance](https://open.kattis.com/problems/segmentdistance) - - [Kattis Segment Intersection](https://open.kattis.com/problems/segmentintersection) - - [Kattis Point in Polygon](https://open.kattis.com/problems/pointinpolygon) - - [Kattis Polygon Area](https://open.kattis.com/problems/polygonarea) - - [Kattis Max Collinear](https://open.kattis.com/problems/maxcolinear) - - Misc - - [Arpa & Geo](http://codeforces.com/problemset/problem/851/B) - - [Tell Your World](http://codeforces.com/problemset/problem/849/B) - - [Gleb & Pizza](http://codeforces.com/problemset/problem/842/B) - - [Birthday Cake](https://open.kattis.com/problems/birthdaycake) - - [Racing Off Track](https://open.kattis.com/contests/acpc17open/problems/racingofftrack) - - [TopCoder Watchtower](https://community.topcoder.com/stat?c=problem_statement&pm=2014&rd=4685) - -## Sweep Line - -### Tutorial - - - CPH 30 - - [TopCoder Line Sweep](https://www.topcoder.com/community/competitive-programming/tutorials/line-sweep-algorithms/) - -### Problems - - - [Cow Steepchase II (Silver)](http://www.usaco.org/index.php?page=viewproblem2&cpid=943) - - :| - - [Kattis Closest Pair](https://open.kattis.com/problems/closestpair2) diff --git a/content/6_Plat/HLD.mdx b/content/6_Plat/HLD.mdx deleted file mode 100644 index b71a7dd..0000000 --- a/content/6_Plat/HLD.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -id: hld -title: "Heavy-Light Decomposition" -author: Benjamin Qi -prerequisites: - - Gold - Euler Tour on Tree - - Platinum - Range Update Range Query -description: Path and subtree updates and queries. -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("YS","Vertex Set Path Composite","vertex_set_path_composite", "Normal", false, ["HLD"], ""), - ], - general: [ - new Problem("Plat", "Disrupt", "842", "Easy", false, ["HLD"]), - ], - } -}; - -<problems-list problems={metadata.problems.sample} /> - -### Tutorial - - - [Anudeep2011](https://blog.anudeep2011.com/heavy-light-decomposition/) - - [AI-Cash](http://codeforces.com/blog/entry/22072) - - [adamant](https://codeforces.com/blog/entry/53170) - -### Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/6_Plat/Hull.mdx b/content/6_Plat/Hull.mdx deleted file mode 100644 index 9d4c6f0..0000000 --- a/content/6_Plat/Hull.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -id: hull -title: "Convex Hull" -author: Benjamin Qi -description: Smallest convex polygon containing a set of points on a grid. -prerequisites: - - Platinum - Geometry Primitives -frequency: 2 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("Kattis", "Convex Hull", "convexhull", "Intro", false, ["convex"], ""), - ], - sample2: [ - new Problem("Kattis","Robert Hood", "roberthood", "Intro", false, ["convex"], "doesn't require convex hull since coordinates are small"), - ], - general: [ - new Problem("Plat", "Balance Beam", "864", "Easy", false, ["convex"], ""), - new Problem("CF", "Geometers Anonymous", "problemset/problem/1195/F", "Normal", false, ["convex", "PURS"], ""), - new Problem("Old Gold", "Cow Curling", "382", "Normal", false, ["convex"], "testing whether points are inside hull"), - new Problem("AC", "AGC E - Random Pawn", "https://atcoder.jp/contests/agc044/tasks/agc044_e", "Very Hard", false, ["convex"], ""), - ], - rotating: [ - new Problem("Kattis", "Fence Orthogonality", "fenceortho", "Normal", false, ["convex"], "enclosing rectangle"), - ], - cht: [ - new Problem("Plat", "Circular Barn", "626", "Normal", false, ["DP", "convex"], ""), - new Problem("Plat", "Falling Portals", "998", "Hard", false, ["convex"], ""), - ], - } -}; - -## [Convex Hull](https://en.wikipedia.org/wiki/Convex_hull_algorithms) - -<problems-list problems={metadata.problems.sample} /> - -### Tutorial - - - Graham Scan - - [cp-algo](https://cp-algorithms.com/geometry/grahams-scan-convex-hull.html) - - [My Implementation](https://github.com/bqi343/USACO/blob/master/Implementations/content/geometry%20(13)/Polygons/ConvexHull2.h) - - Monotone Chain - - CPH 30.3 (brief) - - [Wikipedia](https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain) - - [My Implementation](https://github.com/bqi343/USACO/blob/master/Implementations/content/geometry%20(13)/Polygons/ConvexHull%20(13.2).h) - -<problems-list problems={metadata.problems.general} /> - -## Rotating Caliphers - - -<problems-list problems={metadata.problems.sample2} /> - -diameter of point set: - - - [CF Comment](https://codeforces.com/blog/entry/46162) - -<problems-list problems={metadata.problems.rotating} /> - -## Convex Hull Trick - - - [CF Tutorial](https://codeforces.com/blog/entry/63823) - -<problems-list problems={metadata.problems.cht} />
\ No newline at end of file diff --git a/content/6_Plat/LC.mdx b/content/6_Plat/LC.mdx deleted file mode 100644 index 9bafae3..0000000 --- a/content/6_Plat/LC.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: LC -title: "LineContainer" -author: Benjamin Qi -description: Convex Container -prerequisites: - - Platinum - Convex Hull -frequency: 1 ---- - -## Half-Plane Intersection / Convex Stuff - - - [Blogewoosh (Half-Plane Intersection w/ Ternary Search)](https://codeforces.com/blog/entry/61710) - - [retrograd Half-Plane Intersection](https://codeforces.com/blog/entry/61710?#comment-457662) - - [Petr (Linear Half-Plane Intersection)](https://petr-mitrichev.blogspot.com/2016/07/a-half-plane-week.html) - - [KACTL LineContainer](https://github.com/kth-competitive-programming/kactl/blob/master/content/data-structures/LineContainer.h) - - [Lichao Segment Tree](http://codeforces.com/blog/entry/51275?#comment-351510) - -### Problems - - - https://judge.yosupo.jp/problem/line_add_get_min - - https://judge.yosupo.jp/problem/segment_add_get_min - - [Bridges](https://csacademy.com/contest/archive/task/building-bridges/) - - direct application of LineContainer - - - [USACO Old Gold - Fencing](http://www.usaco.org/index.php?page=viewproblem2&cpid=534) - -(add ICPC probs) - diff --git a/content/6_Plat/More_DFS.mdx b/content/6_Plat/More_DFS.mdx deleted file mode 100644 index ab5ee70..0000000 --- a/content/6_Plat/More_DFS.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -id: more-dfs -title: "More on DFS" -author: Benjamin Qi -prerequisites: - - Gold - Topological Sort -description: Includes 2-edge-connected components, strongly connected components, and biconnected components. -frequency: 1 ---- - -*Some problems sourced from [here](http://codeforces.com/blog/entry/54526?#comment-385354).* - - - [CF DFS Tree + Bridges](https://codeforces.com/blog/entry/68138) - -## 2-Edge Connected Components - -[yosupo](https://judge.yosupo.jp/problem/two_edge_connected_components) - -### Problems - - - SRM 787 1000 - -## Strongly Connected Components - -### Standard - - - [CSES Planets & Kingdoms](https://cses.fi/problemset/task/1683) - - find SCCs - - [Giant Pizza](https://cses.fi/problemset/task/1684) - - 2SAT - -### Tutorial - - - Wikipedia - - [Tarjan](https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm) - - [Kosaraju](https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm) - - CPH (17, Strong Connectivity) - - [CPC.7](https://github.com/SuprDewd/T-414-AFLV/tree/master/07_graphs_1) - - [CF 2-SAT](http://codeforces.com/blog/entry/16205) - - -### Problems - - - [Coin Collector](https://cses.fi/problemset/task/1686) - - DP on SCCs - - [USACO Old Gold: Grass](http://www.usaco.org/index.php?page=viewproblem2&cpid=516) - - [Proving Equivalences](https://open.kattis.com/problems/equivalences) [](78) - - [Festival](https://szkopul.edu.pl/problemset/problem/p9uJo01RR9ouMLLAYroFuQ-7/site/?key=statement) [](173) - -## [Biconnected Components](https://en.wikipedia.org/wiki/Biconnected_component) - -Related topics include - - - Articulation Points - - Bridges - - Block-Cut Tree - -(implementation) - -### Tutorial - - - [GeeksforGeeks](http://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/) - -### Problems - - - [CSES Forbidden Cities](https://cses.fi/problemset/task/1705) - - compute block-cut tree - - [USACO Plat: Push a Box](http://www.usaco.org/index.php?page=viewproblem2&cpid=769) - - [Blockade](https://szkopul.edu.pl/problemset/problem/eDt8w290owtatmCjad0O0ywk/site/?key=statement) - - [POLICIJA](http://wcipeg.com/problem/coi06p2) - - [One-Way Streets](https://csacademy.com/contest/archive/task/one-way-streets/) - - [Investment](https://dmoj.ca/problem/tle17c1p6) - - CEOI problem? diff --git a/content/6_Plat/Olympiads.mdx b/content/6_Plat/Olympiads.mdx deleted file mode 100644 index 366b3d2..0000000 --- a/content/6_Plat/Olympiads.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -id: oly -title: "Olympiads" -author: Benjamin Qi -description: Once you've reached Platinum, it may be helpful to practice with problems from other (inter)national olympiads. ---- - -> Hello, Which online judge should I practice more to do well in **IOI** ? -> the closest OJ for IOI style? -> Do you have any magic problems sets to suggest? - -## National - -See [here](https://ioinformatics.org/page/members/7) for additional links. The [OI Checklist](https://oichecklist.pythonanywhere.com/) is a great way to track your progress. :) - - * [USA](http://www.usaco.org/) - * Format - * Bronze, Silver, Gold, Platinum Divisions - * 3 problems, 4 hrs - * [Very Old Gold Probs](http://tjsct.wikidot.com/usaco/) - * [USACO Training](http://train.usaco.org/usacogate) - * not particularly beginner-friendly but still helpful - * somewhat outdated in the sense that it lacks topics which are now quite common (ex. segment tree) - * if you're unsure about whether it will be useful, you might as well try it and see where you get stuck :P - * personally, I did the first five chapters in one summer (though I had to look up some hints ...) - * [article :o](https://www.usenix.org/legacy/bodinfo/bod/bodmarch10/future.pdf) - * [Japan](https://www.ioi-jp.org/) - * [Open Contests](https://contests.ioi-jp.org/) - * see [oj.uz](https://oj.uz/problems/source/45) - * [Poland](https://szkopul.edu.pl/portal/) - * [Solutions (in Polish)](https://www.oi.edu.pl/l/40/) - * [Canada](https://cemc.math.uwaterloo.ca/contests/computing.html) - * [WCIPEG](https://wcipeg.com/problems/cat%3Dccc%2Cshow%3D50) - * [DMOJ](https://dmoj.ca/problems/?category=24) - * [Croatia](http://hsin.hr/coci/) - * not full feedback - * [Indonesia](https://competition.ia-toki.org/contests) - * monthly - * [Philippines](https://noi.ph/past-problems/) - * Other - * [China (WCIPEG)](https://wcipeg.com/problems/cat%3Dnoi%2Cshow%3D50) - * [Lithuania](http://online.lmio.lt/) - * [Australia](https://orac.amt.edu.au/) - * [Italy](https://training.olinfo.it/#/overview) - -## International - - * IOI - * Online Judges - * [Yandex](https://contest.yandex.com/ioi/) - * [WCIPEG](https://wcipeg.com/problems/cat%3Dioi%2Cshow%3D50) - * [DMOJ](https://dmoj.ca/problems/?category=5) - * [oj.uz](https://oj.uz/problems/source/22) - * Misc - * [List of 2017 IOI Participants](http://weaselcrow.com/pro/cf/ioi2017/) - * [IOI 2018 Syllabus](https://people.ksp.sk/~misof/ioi-syllabus/ioi-syllabus.pdf) - * [Baltic](http://www.boi2017.org/) (BOI) - * [Central European](http://ceoi.inf.elte.hu/) (CEOI) - * submit BOI / CEOI at [CSES](https://cses.fi/) - * [Balkan](http://boi2018.ro/home) - * [International Zhautykov](https://oj.uz/problems/source/24) (IZhO) - * [Asia-Pacific](http://apio-olympiad.org/) (APIO) - * [International Informatics Olympiad in Teams](http://ioit.altervista.org/2018-teams-and-contests-.html) (IOIT) - * [InfO(1) cup](http://info1cup.com/) diff --git a/content/6_Plat/RURQ.mdx b/content/6_Plat/RURQ.mdx deleted file mode 100644 index 36c7623..0000000 --- a/content/6_Plat/RURQ.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -id: RURQ -title: "Range Update Range Query" -author: Benjamin Qi -prerequisites: - - Gold - Point Update Range Query -description: Lazy updates on segment trees and two binary indexed trees in conjunction. -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - bitRevisited: [ - new Problem("CSES", "Polynomial Queries", "1736", "Normal", false, ["BIT-Range"], "equivalent to https://dmoj.ca/problem/acc3p4"), - ], - lazySegTree: [ - new Problem("CSES", "Range Updates & Sums", "1735", "Easy", false, ["Lazy SegTree"], ""), - new Problem("YS", "Range Affine Range Sum", "range_affine_range_sum", "Easy", false, ["Lazy SegTree"], ""), - new Problem("Plat", "Counting Haybales", "578", "Easy", false, ["Lazy SegTree"], ""), - new Problem("CSES", "Old Gold - The Lazy Cow", "418", "Easy", false, ["Lazy SegTree"], ""), - new Problem("CSES", "Area of Rectangles", "1741", "Hard", false, ["Lazy SegTree"], "use segment tree that keeps track of minimum and # of minimums"), - ], - segTreeBeats: [ - new Problem("YS", "Range Chmin Chmax Add Range Sum", "range_chmin_chmax_add_range_sum", "Hard", false, ["SegTreeBeats"], ""), - ], - } -}; - -## BIT Revisited - -Binary Indexed Trees can support range increments in addition to range sum queries. - - - [GFG Range Update Point Query](https://www.geeksforgeeks.org/binary-indexed-tree-range-updates-point-queries/) - - [GFG Range Update Range Query](https://www.geeksforgeeks.org/binary-indexed-tree-range-update-range-queries/) - - [My Implementation](https://github.com/bqi343/USACO/blob/master/Implementations/content/data-structures/1D%20Range%20Queries%20(9.2)/BITrange.h) - -(problem that's only increment with no sets?) - -<problems-list problems={metadata.problems.bitRevisited} /> - -## Lazy Segment Tree - -### Tutorial - - - CPH 28.1 (Segment Trees Revisited) - -<problems-list problems={metadata.problems.lazySegTree} /> - -## Segment Tree Beats - -### Tutorial - - - [CF - jiry2](https://codeforces.com/blog/entry/57319) - -<problems-list problems={metadata.problems.segTreeBeats} /> - -(300iq problem) diff --git a/content/6_Plat/SPneg.mdx b/content/6_Plat/SPneg.mdx deleted file mode 100644 index 68c3f75..0000000 --- a/content/6_Plat/SPneg.mdx +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: sp-neg -title: "Shortest Paths with Negative Edge Weights" -author: Benjamin Qi -prerequisites: - - Gold - Shortest Paths with Non-Negative Edge Weights -description: Applications of Bellman-Ford. -frequency: 1 ---- - - - Hasn't appeared in recent USACO Gold as far as I know. - - If no negative cycles, can use [Shortest Path Faster Algorithm](https://en.wikipedia.org/wiki/Shortest_Path_Faster_Algorithm) or modify Dijkstra slightly (though the same running time bound no longer applies). - -### Tutorial - - - CPH 13.1 - - [cp-algo Bellman Ford](https://cp-algorithms.com/graph/bellman_ford.html) - - [Topcoder Graphs Pt 3](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-graphs-and-their-data-structures-section-3/) - -### Problems - - - General - - [SPOJ Arbitrage](https://www.spoj.com/problems/ARBITRAG/) - - [Kattis APSP (with negative weights)](https://open.kattis.com/problems/allpairspath) - - [CSES High Score](https://cses.fi/problemset/task/1673) - - [Kattis SSSP Negative](https://open.kattis.com/problems/shortestpath3) - - [CSES (Negative) Cycle Finding](https://cses.fi/problemset/task/1197) - -## Simple Linear Programming - -You can also use shortest path algorithms to solve the following problem (a very simple [linear program](https://en.wikipedia.org/wiki/Linear_programming)). - -> Given variables $x_1,x_2,\ldots,x_N$ with constraints in the form $x_i-x_j\ge c$, compute a feasible solution. - - - [Linear Programming Trick](https://www.cs.rit.edu/~spr/COURSES/ALG/MIT/lec18.pdf) - -### Problems - - - [Restore Array](https://oj.uz/problem/view/RMI19_restore) - - [Art](https://codeforces.com/gym/102394/problem/A) (basically same as above) - - Timeline (Camp) - - equivalent to [Timeline (Gold)](http://www.usaco.org/index.php?page=viewproblem2&cpid=1017) except $N,C\le 5000$ and negative values of $x$ are possible.
\ No newline at end of file diff --git a/content/6_Plat/Slope.mdx b/content/6_Plat/Slope.mdx deleted file mode 100644 index 3561e54..0000000 --- a/content/6_Plat/Slope.mdx +++ /dev/null @@ -1,204 +0,0 @@ ---- -id: slope -title: "Slope Trick" -author: Benjamin Qi -prerequisites: - - Platinum - Convex Hull -description: Ways to manipulate piecewise linear convex functions. -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - general: [ - new Problem("CF", "Bookface", "gym/102576/problem/C", "Easy", false, ["Slope Trick"], ""), - new Problem("CC", "CCDSAP Exam", "CCDSAP", "Easy", false, ["Slope Trick"], ""), - new Problem("CF", "Farm of Monsters", "gym/102538/problem/F", "Hard", false, ["Slope Trick"], ""), - new Problem("CF", "Moving Walkways", "contest/1209/problem/H", "Hard", false, ["Slope Trick"], ""), - new Problem("CF", "April Fools' Problem", "contest/802/problem/O", "Very Hard", false, ["Slope Trick"], "binary search on top of slope trick"), - new Problem("ICPC World Finals", "Conquer the World", "https://icpc.kattis.com/problems/conquertheworld", "Very Hard", false, ["Slope Trick"], "ICPC world finals, 0 solves in contest - \"Potatoes\" on tree!!"), - ], - } -}; - -**Slope trick** refers to manipulating piecewise linear convex functions. Includes a simple solution to [Landscaping](http://www.usaco.org/index.php?page=viewproblem2&cpid=650). - -## Tutorials - - - [zscoder](https://codeforces.com/blog/entry/47821) - - [Kuroni](https://codeforces.com/blog/entry/77298) - -From the latter link (modified): - -> Slope trick is a way to represent a function that satisfies the following conditions: -> -> - It can be divided into multiple sections, where each section is a linear function (usually) with an integer slope. -> - It is a convex/concave function. In other words, the slope of each section is non-decreasing or non-increasing when scanning the function from left to right. - -It's generally applicable as a DP optimization. Usually you can come up with a slower DP (ex. $O(N^2)$) first and then optimize it to $O(N\log N)$ with slope trick. The rest of this module assumes that you are somewhat familiar with at least one of the tutorials mentioned above. - -## [Buy Low Sell High](https://codeforces.com/contest/866/problem/D) - -**Slow Solution**: Let $dp[i][j]$ denote the maximum amount of money you can have on day $i$ if you have exactly $j$ shares of stock on that day. The final answer will be $dp[N][0]$. This easily leads to an $O(N^2)$ DP. - -Of course, we never used the fact that the DP is concave down! Specifically, let $dif[i][j]=dp[i][j]-dp[i][j+1]\ge 0$. Then $dif[i][j]\le dif[i][j+1]$ for all $j\ge 0$ (ignoring the case when we get $dp$ values of $-\infty$). - -We'll process the shares in order. Suppose that on the current day shares are worth $p$. We can replace (buy or sell a share) in the statement with (buy, then sell between 0 and 2 shares). - - * If we currently have $j$ shares and overall balance $b$, then after buying, $j$ increases by one and $b$ decreases by $p$. The differences between every two consecutive elements do not change. - * If we choose to buy a share, this is equivalent to setting $dp[i][j]=\max(dp[i][j],dp[i][j+1]+p)$ for all $j$. By the concavity condition, $dp[i][j]=dp[i][j+1]+p$ will hold for all $j$ less than a certain threshold while $dp[i][j+1]$ will hold for all others. So this is equivalent to inserting $p$ into the list of differences while maintaining the condition that the differences are in sorted order. - * So we add $p$ to the list of differences two times. After that, we should pop the smallest difference in the list because we can't end up with a negative amount of shares. - -(insert diagram) - -(insert example) - -The implementation is quite simple; maintain a priority queue that allows you to pop the minimum element. - -<spoiler title="My Solution"> - -```cpp -#include <bits/stdc++.h> -using namespace std; - -int main() { - int N; cin >> N; - priority_queue<int,vector<int>,greater<int>> pq; - long long ans = 0; - for (int i = 0; i < N; ++i) { - int p; cin >> p; ans -= p; - pq.push(p); pq.push(p); pq.pop(); - } - for (int i = 0; i < N; ++i) { - ans += pq.top(); - pq.pop(); - } - cout << ans << "\n"; -} -``` -</spoiler> - -### Extension - -*Stock Trading (USACO Camp)*: What if your amount of shares can go negative, but you can never have more than $L$ shares or less than $-L$? - -## [Potatoes](https://oj.uz/problem/view/LMIO19_bulves) - -[Equivalent Problem](https://atcoder.jp/contests/kupc2016/tasks/kupc2016_h) - -Let $dif_i=a_i-b_i$. Defining $d_j=\sum_{i=1}^jdif_i$, our goal is to move around the potatoes such that $d_0,d_1,\ldots,d_N$ is a non-decreasing sequence. Moving a potato is equivalent to changing exactly one of the $d_i$ (aside from $d_0,d_N$) by one. - -**Slow Solution:** Let $dp[i][j]$ be the minimum cost to determine $d_0,d_1,\ldots,d_i$ such that $d_i\le j$ for each $0\le j\le d_N$. This gives a $O(N\cdot d_N)$ solution. - -As before, this DP is concave up for a fixed $i$! Given a piecewise linear function $DP_x$, we need to support the following operations. - - * Add $|x-k|$ to the function for some $k$ - * Set $DP_x=\min(DP_x,DP_{x-1})$ for all $x$ - -Again, these can be done with a priority queue in $O(N\log N)$ time! - -<spoiler title="My Solution"> - -```cpp -#include <bits/stdc++.h> -using namespace std; - -typedef long long ll; - -int N; -ll fst = 0; // value of DP function at 0 -priority_queue<ll> points; // points where DP function changes slope - -int main() { - cin >> N; - vector<ll> dif(N+1); - for (int i = 1; i <= N; ++i) { - int a,b; cin >> a >> b; - dif[i] = a-b+dif[i-1]; - } - assert(dif[N] >= 0); - for (int i = 1; i < N; ++i) { - if (dif[i] < 0) fst -= dif[i], dif[i] = 0; - fst += dif[i]; - points.push(dif[i]); points.push(dif[i]); - points.pop(); - } - while (points.size()) { - ll a = points.top(); points.pop(); - fst -= min(a,dif[N]); - } - cout << fst << "\n"; -} -``` -</spoiler> - -## [Landscaping](http://www.usaco.org/index.php?page=viewproblem2&cpid=650) - -Equivalent Problem: GP of Wroclaw 20 J - -This is quite similar to the previous task, so it's easy to guess that slope trick is applicable. - -Again, let's first come up with a slow DP. Let $dp[i][j]$ equal the number of ways to move dirt around the first $i$ flowerbeds such that the first $i-1$ flowerbeds all have the correct amount of dirt while the $i$-th flowerbed has $j$ extra units of dirt (or lacks $-j$ units of dirt if $j$ is negative). The answer will be $dp[N][0]$. - -This DP is concave up for any fixed $i$. To get $dp[i+1]$ from $dp[i]$ we must be able to support the following operations. - - * Shift the DP curve $A_i$ units to the right. - * Shift the DP curve $B_i$ units to the left. - * Add $Z\cdot |j|$ to $DP[j]$ for all $j$. - * Set $DP[j] = \min(DP[j],DP[j-1]+X)$ and $DP[j] = \min(DP[j],DP[j+1]+Y)$ for all $j$. - -As before, it helps to look at the differences $dif[j]=DP[j+1]-dif[j]$ instead. Then the last operation is equivalent to the following: - - * For all $j\ge 0$, we set $dif[j] = \min(dif[j]+Z,X)$ - * For all $j<0$, we set $dif[j] = \max(dif[j]-Z,-Y)$. - -If we maintain separate deques for $dif$ depending on whether $j\ge 0$ or $j<0$ and update all of the differences in the deques "lazily" then we can do this in $O(\sum A_i+\sum B_i)$ time. - -Bonus: Solve this problem when $\sum A_i+\sum B_i$ is not so small. - -<spoiler title="My Solution"> - -```cpp -#include <bits/stdc++.h> - -using namespace std; - -int N,X,Y,Z; -int difl, difr; -deque<int> L, R; -long long ans; - -void rig() { // shift right A - if (L.size() == 0) L.push_back(-Y-difl); - int t = L.back()+difl; L.pop_back(); - t = max(t,-Y); ans -= t; - R.push_front(t-difr); -} - -void lef() { // shift left B - if (R.size() == 0) R.push_front(X-difr); - int t = R.front()+difr; R.pop_front(); - t = min(t,X); ans += t; - L.push_back(t-difl); -} - -int main() { - freopen("landscape.in","r",stdin); - freopen("landscape.out","w",stdout); - cin >> N >> X >> Y >> Z; - for (int i = 0; i < N; ++i) { - int A,B; cin >> A >> B; - for (int j = 0; j < A; ++j) rig(); // or we can just do |A-B| shifts in one direction - for (int j = 0; j < B; ++j) lef(); - difl -= Z, difr += Z; // adjust slopes differently for left and right of j=0 - } - cout << ans << "\n"; -} -``` -</spoiler> - -## Problems - -<problems-list problems={metadata.problems.general} />
\ No newline at end of file diff --git a/content/6_Plat/String_Search.mdx b/content/6_Plat/String_Search.mdx deleted file mode 100644 index 6318c29..0000000 --- a/content/6_Plat/String_Search.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -id: string-search -title: "String Searching" -author: Benjamin Qi -prerequisites: - - Silver - Depth First Search -description: Knuth-Morris-Pratt and Z Algorithms (and a few more related topics). -frequency: 1 ---- - -## General Resources - - - [CPC.11](https://github.com/SuprDewd/T-414-AFLV/tree/master/11_strings) - - [CP-Algorithms String Processing: Fundamentals](https://cp-algorithms.com/) - -## Z, KMP - - - [yosupo - Z](https://judge.yosupo.jp/problem/zalgorithm) - - Tutorial - - CPH 26.4 (Z-algorithm) - - [PAPS 14.2](https://www.csc.kth.se/~jsannemo/slask/main.pdf) - - [paladin8](http://codeforces.com/blog/entry/3107) - - [GeeksForGeeks](http://www.geeksforgeeks.org/searching-for-patterns-set-2-kmp-algorithm/) - - [TopCoder](https://www.topcoder.com/community/data-science/data-science-tutorials/introduction-to-string-searching-algorithms/) - -## Manacher - - - Has appeared at camp but not in platinum. - - [HackerRank](https://www.hackerrank.com/topics/manachers-algorithm) - - [adamant](http://codeforces.com/blog/entry/12143) - - [cp-algorithms](https://cp-algorithms.com/string/manacher.html) - -## Aho-Corasick - - - Has appeared in old gold. - - Tutorial - - [adamant](http://codeforces.com/blog/entry/14854) - - [GeeksForGeeks](http://www.geeksforgeeks.org/aho-corasick-algorithm-pattern-searching/) - -## Palindromic Tree (Intro) - -[Palindromic Tree](http://codeforces.com/blog/entry/13959) - -APIO Palindrome
\ No newline at end of file diff --git a/content/6_Plat/Suffix_Array.mdx b/content/6_Plat/Suffix_Array.mdx deleted file mode 100644 index 0779efb..0000000 --- a/content/6_Plat/Suffix_Array.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: suffix-array -title: "Suffix Array" -author: Benjamin Qi -description: Sorting Suffixes of a String -prerequisites: - - Gold - Hashing -frequency: 1 ---- - -https://judge.yosupo.jp/problem/suffixarray -https://judge.yosupo.jp/problem/number_of_substrings - - - [USACO Plat - Standing Out from the Herd](http://www.usaco.org/index.php?page=viewproblem2&cpid=768) - -## Inverse Burrows-Wheeler - -## Run Enumerate - -https://judge.yosupo.jp/problem/runenumerate
\ No newline at end of file diff --git a/content/6_Plat/Tries.mdx b/content/6_Plat/Tries.mdx deleted file mode 100644 index 85966d9..0000000 --- a/content/6_Plat/Tries.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -id: tries -title: "Tries" -author: Benjamin Qi -description: ? -frequency: 1 ---- - -import { Problem } from "../models"; - -export const metadata = { - problems: { - sample: [ - new Problem("YS", "Set XOR-Min", "set_xor_min", "Normal", false, [], ""), - ], - } -}; - -<problems-list problems={metadata.problems.sample} /> - - - CPH 26.2 - - [Algorithm Gym](http://codeforces.com/blog/entry/15729) diff --git a/content/7_Advanced/String_Suffix.mdx b/content/7_Advanced/String_Suffix.mdx deleted file mode 100644 index 1cb8e58..0000000 --- a/content/7_Advanced/String_Suffix.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: string-suffix -title: "String Suffix Structures" -author: Benjamin Qi -description: "?" -prerequisites: - - Platinum - String Searching -frequency: 0 ---- - - * String Suffix Structures - * [Suffix Automata](http://codeforces.com/blog/entry/20861) - * Suffix Tree - * [CF](http://codeforces.com/blog/entry/16780) - * [CP-Algo](https://cp-algorithms.com/string/suffix-tree-ukkonen.html) - * O(nlogn) suffix array usually suffices - * More on Palindromic Tree - * [Palindrome Partition](https://codeforces.com/contest/932/problem/G) - * [Partial Solution](https://codeforces.com/blog/entry/19193) - * [Palindromic Magic (HARD)](https://codeforces.com/contest/1081/problem/H) diff --git a/content/7_Advanced/Treaps.mdx b/content/7_Advanced/Treaps.mdx deleted file mode 100644 index 6cd0bcd..0000000 --- a/content/7_Advanced/Treaps.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: treaps -title: "Treaps" -author: Benjamin Qi -description: "?" -prerequisites: - - Platinum - Range Update Range Query -frequency: 2 ---- - -<info-block title="Pro Tip"> -Historically restricted to USACO Camp (aside from the exception below). -</info-block> - -## Tutorial - - - [Quora](https://threads-iiith.quora.com/Treaps-One-Tree-to-Rule-em-all-Part-1) - - [PPT](https://docs.google.com/presentation/d/14xgtdDWnIBwmJRAuIdZ8FvLZcX9uRxnNoGOGAQRDIvc/edit?usp=sharing) - - Samuel Hsiang Guide (see resources) - -### Other - - - [Euler Tour Tree](https://codeforces.com/blog/entry/18369) - - [Merging Segment Trees](https://codeforces.com/blog/entry/49446) - -## Problems - - - [Old Gold - Airplane Boarding](http://www.usaco.org/index.php?page=viewproblem2&cpid=402) - - [Strings](https://csacademy.com/contest/archive/task/strings/) [](181) - - [Points & Distances](https://www.hackerearth.com/problem/algorithm/septembereasy-points-and-distances-d30d0e6b/description/) [](185) - - [Tree Rotations 2](https://szkopul.edu.pl/problemset/problem/b0BM0al2crQBt6zovEtJfOc6/site/?key=statement) [](193) - -A2OJ: https://a2oj.com/category?ID=14 - - - ?
\ No newline at end of file diff --git a/content/authors/authors.ts b/content/authors/authors.ts new file mode 100644 index 0000000..3db10c9 --- /dev/null +++ b/content/authors/authors.ts @@ -0,0 +1,78 @@ +export type Author = { + photo: string; // url of photo relative to content/authors/images/, EXCLUDING EXTENSION + name: string; + title: string; + blurb: string; + email?: string; + facebook?: string; + linkedin?: string; + github?: string; + website?: string; + codeforces?: string; +}; + +export const Authors: Author[] = [ + { + photo: 'nathanw', + name: 'Nathan Wang', + title: 'Coordinator & Webmaster', + blurb: 'Nathan Wang is a two-time USACO Finalist and web development enthusiast.', + website: "https://thecodingwizard.me/", + codeforces: 'thecodingwizard', + email: "nathan.r.wang@gmail.com", + github: "thecodingwizard", + }, + // { + // photo: 'briancdean', + // name: 'Brian C. Dean', + // title: 'Content Director', + // blurb: 'Brian Christopher Dean is the director of the USA Computing Olympiad.', + // email: 'bcdean@clemson.edu', + // }, + { + photo: 'benq', + name: 'Benjamin Qi', + title: 'Content Director', + blurb: 'Benjamin Qi is a two-time IOI winner, USACO Problemsetter, and part of the MIT Class of 2023.', + codeforces: "Benq", + email: "bqi343@gmail.com", + github: "bqi343" + }, + { + photo: 'michael', + name: 'Michael Cao', + title: 'Content Author', + blurb: 'Michael Cao is a USACO Platinum competitor and problemsetter of "Bessie\'s Snow Cow" and "Flooded Farm."', + facebook: "mike.lao.9400", + codeforces: "caoash", + email: "cao.michael14@gmail.com", + github: "caoash", + }, + { + photo: 'darren', + name: 'Darren Yao', + title: 'Content Author', + blurb: 'Darren Yao is a USACO Platinum competitor and author of the textbook "An Introduction to the USA Computing Olympiad."', + website: 'http://darrenyao.com/', + email: 'darren.yao@gmail.com', + codeforces: "darren_yao", + github: "darren-yao", + }, + { + photo: 'nathanc', + name: 'Nathan Chen', + title: 'Content Author', + blurb: 'Nathan Chen is a two-time USACO Finalist who competes in both Java and C++ fluently.', + codeforces: "nchn27", + github: "nchn27", + email: "nchn27@gmail.com", + }, + { + photo: 'cow', + name: 'Siyong Huang', + title: 'Content Author', + blurb: 'Siyong Huang is a two-time USACO Finalist.', + codeforces: "frodakcin", + github: "frodakcin", + }, +]; diff --git a/content/authors/images/benq.png b/content/authors/images/benq.png Binary files differnew file mode 100644 index 0000000..dadac68 --- /dev/null +++ b/content/authors/images/benq.png diff --git a/content/authors/images/briancdean.jpg b/content/authors/images/briancdean.jpg Binary files differnew file mode 100644 index 0000000..875b20b --- /dev/null +++ b/content/authors/images/briancdean.jpg diff --git a/content/authors/images/cow.png b/content/authors/images/cow.png Binary files differnew file mode 100644 index 0000000..d7abfac --- /dev/null +++ b/content/authors/images/cow.png diff --git a/content/authors/images/darren.jpeg b/content/authors/images/darren.jpeg Binary files differnew file mode 100644 index 0000000..2c8a329 --- /dev/null +++ b/content/authors/images/darren.jpeg diff --git a/content/authors/images/michael.jpg b/content/authors/images/michael.jpg Binary files differnew file mode 100644 index 0000000..69d3b30 --- /dev/null +++ b/content/authors/images/michael.jpg diff --git a/content/authors/images/nathanc.jpeg b/content/authors/images/nathanc.jpeg Binary files differnew file mode 100644 index 0000000..ca01a2b --- /dev/null +++ b/content/authors/images/nathanc.jpeg diff --git a/content/authors/images/nathanw.jpg b/content/authors/images/nathanw.jpg Binary files differnew file mode 100644 index 0000000..9882167 --- /dev/null +++ b/content/authors/images/nathanw.jpg diff --git a/content/models.ts b/content/models.ts index 60bb14b..1a584ac 100644 --- a/content/models.ts +++ b/content/models.ts @@ -1,39 +1 @@ -const sources = { - "CC": "https://www.codechef.com/problems/", - "CSA": "https://csacademy.com/contest/archive/task/", - "DMOJ": "https://dmoj.ca/problem/", - "SPOJ": "https://www.spoj.com/problems/", - "YS": "https://judge.yosupo.jp/problem/", - "CF": "https://codeforces.com/", - "Bronze": "http://www.usaco.org/index.php?page=viewproblem2&cpid=", - "Silver": "http://www.usaco.org/index.php?page=viewproblem2&cpid=", - "Gold": "http://www.usaco.org/index.php?page=viewproblem2&cpid=", - "Old Bronze": "http://www.usaco.org/index.php?page=viewproblem2&cpid=", - "Old Silver": "http://www.usaco.org/index.php?page=viewproblem2&cpid=", - "Old Gold": "http://www.usaco.org/index.php?page=viewproblem2&cpid=", - "Plat": "http://www.usaco.org/index.php?page=viewproblem2&cpid=", - "Kattis": "https://open.kattis.com/problems/", - "CSES": "https://cses.fi/problemset/task/", - "LC": "https://leetcode.com/problems/", - "ojuz": "https://oj.uz/problem/view/", -}; - -export class Problem { - public url: string; - - constructor( - public source: string, - public name: string, - public id: string, - public difficulty?: 'Intro' | 'Easy' | 'Normal' | 'Hard' | 'Very Hard' | 'Insane', - public starred?: boolean, - public tags?: string[], - public sketch?: string, - ) { - if (!id.startsWith("http")) { - if (source in sources) { - this.url = sources[source] + id; - } else throw `URL ${id} is not valid. Did you make a typo in the problem source (${source}), or in the URL? Problem name: ${name}` - } else this.url = id; - } -} +export { Problem } from "../src/models/problem";
\ No newline at end of file diff --git a/content/ordering.ts b/content/ordering.ts index 0c457b8..de6f764 100644 --- a/content/ordering.ts +++ b/content/ordering.ts @@ -1,92 +1,169 @@ -export type ModuleOrderingID = string; -export type ModuleOrderingGroup = { +// Section -> Chapter -> Module + +export type SectionID = "intro" | "bronze" | "silver" | "gold" | "plat" | "adv"; + +export type Chapter = { name: string; - items: ModuleOrderingItem[]; -}; -export type ModuleOrderingItem = ModuleOrderingID | ModuleOrderingGroup; -export const isModuleOrderingGroup = (x: ModuleOrderingItem): x is ModuleOrderingGroup => x.hasOwnProperty("name"); + items: string[]; + description?: string; +} -const ModuleOrdering: {[key: string]: ModuleOrderingItem[]} = { +const MODULE_ORDERING: {[key in SectionID]: Chapter[]} = { "intro": [ - "intro", - "lang", - "running-cpp", - "data-types", - "io", - "ex-prob", - ], - "general": [ - "resources", - "practicing", - "contests", - "contest-strategy", - "proposing", + { + name: "About This Guide", + description: "In this first chapter, you'll learn about how this guide is structured and how best to use this guide.", + items: [ + "using-this-guide", + "modules", + "code-con", + ] + }, + { + name: "Getting Started", + description: "Start here if you're new to competitive programming.", + items: [ + "intro", + "choosing-lang", + "running-code", + "data-types", + "io", + "expected", + ] + }, + { + name: "General", + items: [ + "resources", + "practicing", + "contest-strategy", + "contests", + "oly", + ] + }, { name: "Language-Specific", items: [ - "why-cpp", - "macros", + "factors-choosing", + "fast-io", "debugging", + "cpp-tips", + "lambda", + ] + }, + { + name: "USACO", + items: [ + "usaco-camp", ] }, ], "bronze": [ - "time-comp", - "rect-geo", + { + name: "Basics", + items: [ + "time-comp", + "simulation", + "rect-geo", + ] + }, { name: "Data Structures", items: [ - "collections", - "containers", + "intro-ds", "pairs-tuples", - "ds", - "intro-sorting", ] }, - "simulation", - "complete-search", - "intro-graphs", + { + name: "Complete Search", + description: "Solving bronze problems by checking all possible cases in the solution space.", + items: [ + "intro-complete", + "complete-rec", + ] + }, + { + name: "Unusual", + items: [ + "ad-hoc", + "intro-greedy", + "intro-graphs", + ] + } ], "silver": [ - "prefix-sums", + { + name: "Prefix Sums", + items: [ + "prefix-sums", + ] + }, { name: "Sorting", items: [ + "binary-search-sorted", "sorting-custom", - "sorting-cpp", + "greedy", ] }, { - name: "Data Structures", + name: "Two Pointers", + items: [ + "2P", + ] + }, + { + name: "Binary Search", items: [ - "stacks-queues", - "amortized", - "maps-sets", + "binary-search-ans", + ] + }, + { + name: "Sets & Maps", + description: "Maintaining collections of distinct elements.", + items: [ + "unordered", + "intro-ordered", + "custom-cpp-stl", ] }, - "binary-search", - "greedy", { name: "Graphs", items: [ "dfs", "ff", "func-graphs", - "bfs", ] }, ], "gold": [ - "dp", - "intro-nt", + { + name: "Data Structures", + items: [ + "stacks", + "sliding", + "queues", + ] + }, + { + name: "Dynamic Programming", + items: [ + "intro-dp", + ] + }, + { + name: "Number Theory", + items: [ + "intro-nt", // does this really belong so high up on the list?? + ] + }, { name: "Graphs", items: [ + "bfs", "toposort", "cyc", - "dsu", "sp", - "mst", ] }, { @@ -95,32 +172,40 @@ const ModuleOrdering: {[key: string]: ModuleOrderingItem[]} = { "SRQ", "springboards", "PURS", - "PURQ", ] }, { name: "Trees", items: [ + "dsu", + "mst", "dp-trees", - "merging", - "bin-jump", "tree-euler", ] }, - "hashing", + { + name: "Hashing", + items: [ + "faster-hashmap", + "string-hashing", + ] + } ], "plat": [ - "oly", { name: "Range Queries", items: [ + "seg-ext", "RURQ", "2DRQ", + "sqrt", ] }, { name: "Trees", items: [ + "bin-jump", + "merging", "hld", "centroid", ] @@ -130,58 +215,90 @@ const ModuleOrdering: {[key: string]: ModuleOrderingItem[]} = { items: [ "dp-bitmasks", "dp-ranges", + "dp-more", ] }, { name: "Graphs", items: [ "sp-neg", - "more-dfs", + "BCC-2CC", + "SCC", + "max-flow", "eulerian-tours", + "offline-con", "eulers-formula", - "max-flow", ] }, { name: "Strings", items: [ - "tries", - "suffix-array", "string-search", + "suffix-array", ] }, { name: "Geometry", items: [ "geo-pri", - "hull", + "sweep-line", + "convex-hull", "LC", - "aliens", + "lagrange", "slope", ] }, - "bitsets", - "fracture", + { + name: "Misc. Topics", + items: [ + "bitsets", + "fracture", + "mat-exp", + ] + } ], "adv": [ - "treaps", - "persistent", - "min-cost-flow", - "critical", - "LCT", - "string-suffix", - "fft", - "game-theory", - "multiplicative", - "matroid-isect", + { + name: "Data Structures", + items: [ + "treaps", + "persistent", + "segtree-beats", + "LCT", + ] + }, + { + name: "Flows", + items: [ + "flow-lb", + "min-cost-flow", + ] + }, + { + name: "Polynomials", + items: [ + "fft", + "fft-ext", + ] + }, + { + name: "Misc. Topics", + items: [ + "extend-euclid", + "critical", + "string-suffix", + "game-theory", + "multiplicative", + "matroid-isect", + ] + } ] }; -export default ModuleOrdering; -export const divisions = Object.keys(ModuleOrdering); -export const divisionLabels = { +export default MODULE_ORDERING; +export const SECTIONS: SectionID[] = Object.keys(MODULE_ORDERING) as SectionID[]; +export const SECTION_LABELS: {[key in SectionID]: string} = { "intro": "Intro", - "general": "General", "bronze": "Bronze", "silver": "Silver", "gold": "Gold", @@ -189,16 +306,26 @@ export const divisionLabels = { "adv": "Advanced", }; -const moduleIDToDivisionMap = {}; +let moduleIDToSectionMap: {[key: string]: SectionID} = {}; -Object.keys(ModuleOrdering).forEach(division => { - const process = module => { - if (module.hasOwnProperty('name')) { - return module.items.forEach(process); - } - moduleIDToDivisionMap[module] = division; - }; - ModuleOrdering[division].forEach(process); +SECTIONS.forEach(section => { + MODULE_ORDERING[section].forEach(category => { + category.items.forEach(moduleID => { + moduleIDToSectionMap[moduleID] = section; + }) + }); +}); + +export { moduleIDToSectionMap }; + +let moduleIDToURLMap: {[key: string]: string} = {}; + +SECTIONS.forEach(section => { + MODULE_ORDERING[section].forEach(category => { + category.items.forEach(moduleID => { + moduleIDToURLMap[moduleID] = `/${section}/${moduleID}`; + }) + }); }); -export { moduleIDToDivisionMap };
\ No newline at end of file +export { moduleIDToURLMap }; diff --git a/docs/Content Documentation.md b/docs/Content Documentation.md new file mode 100644 index 0000000..a57574b --- /dev/null +++ b/docs/Content Documentation.md @@ -0,0 +1,281 @@ +# Content Documentation + +The following two modules are good to read: + +1. https://usaco-guide.netlify.app/intro/modules +2. https://usaco-guide.netlify.app/intro/code-con + +All modules are written in [Markdown](https://www.markdownguide.org/cheat-sheet/). There are special additions to the markdown that we have added to this site. If you are confused about something, or if there's a certain feature that you want to add, reach out to Nathan Wang. + +## 1. Getting Started + +### Running Site Locally + +It is highly recommended to [run a local version of the site](Front%20End%20Documentation.md) in order to view your changes. + +### Using Sublime Text with .mdx + +1. Download [here](https://www.sublimetext.com/). +2. Open the command palette (Cmd-Shift-P) and install package control. +3. Open the command palette again, install package -> PackageResourceViewer +4. Extract the HTML package with PackageResourceViewer. +5. Now you can modify `html_completions.py` by adding to `normal_tags` (ex. `spoiler`) + - actually, for some reason uppercase (`CPPSection`) doesn't work ... +6. Open a `.mdx` file and set syntax highlighting to be the same as `.md` with `View -> Syntax -> Open all with current extension as ... -> Markdown -> Markdown`. +7. Make snippets! + +### Other Tools + +- You can use [StackEdit](https://stackedit.io/) to check that latex renders properly. +- [Markdown Table Generator](https://www.tablesgenerator.com/markdown_tables) + +## 2. Module Ordering + +### Organization + +There are six **sections** in the guide: Intro, Bronze, Silver, Gold, Platinum, and Advanced. Each section is subdivided into **categories**, which is just a group of modules. Each **module** represents one "article" or "page" on the site. + +### `ordering.ts` + +Located at `content/ordering.ts`, this file stores the ordering of the modules. Hopefully the format is self-explanatory (it matches based on "slug"). Let Nathan Wang know if you have questions. + +## 3. Frontmatter + +[Frontmatter](https://jekyllrb.com/docs/front-matter/) is the stuff in the beginning of each module that's surrounded +by three dashes. Frontmatter is written in [YAML](https://yaml.org/). It stores the "metadata" for each module. + +- **ID**: _Required_. The ID of the module. Ex: `getting-started`, or `containers`. This ID is used to identify + the module, so make sure it is **unique** and **all lowercase with dashes only**. The URL will be generated based off this. +- **Title**: _Required_. The title of the module. Ex: `Getting Started` +- **Author**: _Required_. The author of the module. Ex: `Unknown` +- **Description**: _Required_. A short description of the module, similar to what [codecademy](https://www.codecademy.com/learn/paths/computer-science) has in their syllabus. Markdown/Latex does not work in the description field. +- **Prerequisites**: _Optional_. Any prerequisites for this module. If you want to reference a module as a prerequisite, list it as a module ID. A link will be auto-generated. +- **Frequency**: _Optional_. Takes a number 0-4 inclusive, where 0 = never shown up before and 4 = shows up ~once a contest. Leave this field out if you don't want to show the frequency. + +### Example Frontmatter + +``` +--- +id: getting-started +title: Getting Started +description: Welcome to the guide! We'll introduce what programming competitions are and how this guide is organized. +author: Nathan Wang +order: 1 +prerequisites: + - Dummy prerequisite + - running-cpp +--- + +# Getting Started +... +``` + +## 4. Table of Contents + +A table of contents will be auto-generated based off of the headings in the Markdown. Keep this in mind when formatting your module. + +## 5. MDX and Custom Components + +We're using [MDX](https://mdxjs.com/), a superset of Markdown. HTML and React components are supported, so it is possible to add interactivity / custom components to each module. + +Some components are globally available in every module (without having to be imported): + +- `<Spoiler>` +- `<Info>` +- `<Warning>` +- `<Optional>` +- `<Problems>` +- `<Resources>` +- `<Resource>` +- `<TextTooltip>` +- `<LanguageSection>` +- `<CPPSection>` +- `<JavaSection>` +- `<PySection>` +- `<IncompleteSection>` +- `<Asterisk>` + +These are all documented below. + +### Using Custom Components + +Interleaving Markdown in JSX is currently a work-in-progress: https://github.com/mdx-js/mdx/issues/628. As such, in order to use markdown with custom components, we need to leave **blank lines** between the HTML tags and the markdown. + +```mdx +Good: + +<Spoiler> + +This is **bold**. + +</Spoiler> +``` + +```mdx +Bad: + +<Spoiler> +This is NOT **bold**. +</Spoiler> + +<Spoiler>This isn't **bold** either!</Spoiler> +``` + +### Spoilers + +Spoilers are collapsible elements that only show themselves when the user clicks on it. It's useful +when writing solutions to problems. + +``` +<Spoiler title="Show Solution"> + +- Insert OP benq solution here + +</Spoiler> +``` + +### Info Block + +``` +<Info title="Insert Title Here"> + +**Markdown is Supported!!** + +</Info> +``` + +### Warning Block + +``` +<Warning title="Insert Title Here"> + +Fun fact: the title attribute is optional. + +</Warning> +``` + +### Optional Content + +``` +<Optional title="Insert Title Here"> + +Fun fact: the title attribute is optional. + +</Optional> +``` + +### Problem Lists + +Problem constructor: + +```typescript +class Problem { + constructor( + public source: string, + public name: string, + public id: string, + public difficulty?: 'Intro' | 'Easy' | 'Normal' | 'Hard' | 'Very Hard', + public starred?: boolean, + public tags?: string[], + public sketch?: string, + ) {} +} +``` + +Example usage: + +```mdx +--- +id: ds +title: Data Structures +author: Nathan Wang, Darren Yao, Benjamin Qi +description: Introductory problems using sets and maps. +prerequisites: + - Bronze - "Built-In C++ Containers" or "Built-In Java Collections" +--- + +import { Problem } from '../models'; + +export const problems = { + standard: [ + new Problem('YS', 'Associative Array', 'associative_array', 'Intro'), + new Problem('CSES', 'Distinct Numbers', '1621', 'Intro'), + new Problem( + 'CSES', + 'Sum of Two Values', + '1640', + 'Intro', + false, + [], + 'Can be solved without sets.' + ), + new Problem('CSES', 'Concert Tickets', '1091', 'Easy', false, [ + 'iterators', + ]), + new Problem('CSES', 'Towers', '1073', 'Easy', false, [ + 'multiset', + 'greedy', + ]), + new Problem('CSES', 'Traffic Lights', '1163', 'Normal', false, ['set']), + new Problem('CSES', 'Room Allocation', '1164', 'Normal', false, [ + 'multiset', + 'greedy', + ]), + ], +}; + +## Standard + +Do roughly the first half of the Sorting and Searching section in the [CSES Problem Set](https://cses.fi/problemset/). + +<Problems problems={problems.standard} /> +``` + +### Resource Lists + +stub + +### Tooltips + +stub + +#### `<TextTooltip>` + +stub + +#### `<Asterisk>` + +stub + +### Language-Specific Content + +```mdx +<LanguageSection> +<CPPSection> + +# A heading that only appears in C++ + +CPP content goes here, note the newlines! + +</CPPSection> +<JavaSection> + +Java content goes here! + +</JavaSection> +<PySection /> +</LanguageSection> +``` + +In the example above, nothing will be rendered for Python. + +### Incomplete Section + +```mdx +<IncompleteSection> + +- this list is optional and can be used to specify what is missing +- missing 32-bit integer explanation + +</IncompleteSection> +``` diff --git a/Front End Documentation.md b/docs/Front End Documentation.md index 1873b5e..c4c7e4a 100644 --- a/Front End Documentation.md +++ b/docs/Front End Documentation.md @@ -1,6 +1,6 @@ # Front End Documentation -If you are familiar with Gatsby development, just run `yarn install` and `yarn develop` to get started. +If you are familiar with Gatsby development, just run `yarn` and `yarn develop` to get started. ## Quickstart @@ -14,6 +14,12 @@ The following is written for individuals without front-end development experienc 2. Clone repo - `git clone https://github.com/thecodingwizard/usaco-guide.git` 3. Install Dependencies - - `yarn install` + - `yarn` 4. Run development server - `yarn develop` + +## Credits + +- Confetti taken from Josh Comeau: https://github.com/joshwcomeau/react-europe-talk-2018 +- Lots of inspiration from Josh Comeau's blog and Gatsbyjs documentation site +- Syllabus template from https://www.howtographql.com/
\ No newline at end of file diff --git a/gatsby-browser.tsx b/gatsby-browser.tsx index 81c031b..855d01b 100644 --- a/gatsby-browser.tsx +++ b/gatsby-browser.tsx @@ -1,108 +1,11 @@ import './src/styles/main.css'; +import './src/styles/anchor.css'; import * as React from 'react'; -import { MDXProvider } from '@mdx-js/react'; -import { ProblemsListComponent } from './src/components/markdown/Problems'; -import CodeBlock from './src/components/markdown/CodeBlock'; -import SpoilerComponent from './src/components/markdown/SpoilerComponent'; - -const components = { - 'module-excerpt': props => <div {...props} />, - spoiler: SpoilerComponent, - 'info-block': ({ - children, - title, - }: { - children: React.ReactNode; - title: string; - }) => ( - <div className="rounded-md bg-blue-50 p-4 mb-12 info-block"> - <div className="flex"> - <div className="flex-shrink-0"> - <svg - className="h-5 w-5 text-blue-400" - fill="currentColor" - viewBox="0 0 20 20" - > - <path - fillRule="evenodd" - d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" - clipRule="evenodd" - /> - </svg> - </div> - <div className="ml-3"> - <h3 className="info-block__heading">{title}</h3> - <div className="info-block__body">{children}</div> - </div> - </div> - </div> - ), - 'warning-block': ({ children }) => ( - <div className="rounded-md bg-yellow-50 p-4"> - <div className="flex"> - <div className="flex-shrink-0"> - <svg - className="h-5 w-5 text-yellow-400" - viewBox="0 0 20 20" - fill="currentColor" - > - <path - fillRule="evenodd" - d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" - clipRule="evenodd" - /> - </svg> - </div> - <div className="ml-3"> - <h3 className="text-sm leading-5 font-medium text-yellow-800"> - Warning! - </h3> - <div className="mt-2 text-sm leading-5 text-yellow-700"> - <p>{children}</p> - </div> - </div> - </div> - </div> - ), - 'optional-content': ({ - children, - title, - className, - }: { - children: React.ReactNode; - title?: string; - className?: string; - }) => ( - <div - className={`bg-white overflow-hidden shadow rounded-lg border border-purple-400`} - style={{ margin: '1rem 0' }} - > - <div className="p-4 flex items-center font-medium text-purple-800 bg-purple-50"> - <svg className="h-6 w-6 mr-3" fill="currentColor" viewBox="0 0 20 20"> - <path d="M11 3a1 1 0 10-2 0v1a1 1 0 102 0V3zM15.657 5.757a1 1 0 00-1.414-1.414l-.707.707a1 1 0 001.414 1.414l.707-.707zM18 10a1 1 0 01-1 1h-1a1 1 0 110-2h1a1 1 0 011 1zM5.05 6.464A1 1 0 106.464 5.05l-.707-.707a1 1 0 00-1.414 1.414l.707.707zM5 10a1 1 0 01-1 1H3a1 1 0 110-2h1a1 1 0 011 1zM8 16v-1h4v1a2 2 0 11-4 0zM12 14c.015-.34.208-.646.477-.859a4 4 0 10-4.954 0c.27.213.462.519.476.859h4.002z" /> - </svg> - Optional{title ? `: ${title}` : ''} - </div> - <div className="px-4 pt-5 pb-2 sm:p-6 sm:pb-2">{children}</div> - </div> - ), - 'problems-list': ProblemsListComponent, - code: CodeBlock, - inlineCode: props => ( - <code - {...props} - className="font-mono text-sm inline bg-gray-200 rounded px-1 py-05" - /> - ), - table: props => <table {...props} className="text-base border-gray-600" />, - th: props => <th {...props} className="border py-1 px-3" />, - td: props => <td {...props} className="border py-1 px-3" />, - h3: props => ( - <h3 {...props} className="leading-snug text-lg font-semibold mb-4 mt-6" /> - ), - p: props => <p {...props} className="mb-4" />, -}; +import MDXProvider from './src/components/markdown/MDXProvider'; +import { UserDataProvider } from './src/context/UserDataContext'; export const wrapRootElement = ({ element }) => ( - <MDXProvider components={components}>{element}</MDXProvider> + <MDXProvider> + <UserDataProvider>{element}</UserDataProvider> + </MDXProvider> ); diff --git a/gatsby-config.ts b/gatsby-config.ts index 752c0ee..3090c07 100644 --- a/gatsby-config.ts +++ b/gatsby-config.ts @@ -1,6 +1,6 @@ export const siteMetadata = { title: `USACO Guide`, - description: `A collection of curated, high-quality resources to take you from Bronze to Platinum.`, + description: `A free collection of curated, high-quality resources to take you from Bronze to Platinum and beyond.`, author: `@usacoteam`, }; export const plugins = [ @@ -18,11 +18,25 @@ export const plugins = [ }, }, { + resolve: `gatsby-source-filesystem`, + options: { + path: `${__dirname}/src/assets`, + name: `assets`, + }, + }, + `gatsby-plugin-sharp`, + { resolve: `gatsby-plugin-mdx`, options: { extensions: [`.mdx`, `.md`], gatsbyRemarkPlugins: [ - `gatsby-remark-autolink-headers`, + { + resolve: `gatsby-remark-autolink-headers`, + options: { + // icon source: https://joshwcomeau.com/ + icon: `<svg fill="none" height="24" width="24" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle;"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>`, + }, + }, { resolve: `gatsby-remark-katex`, options: { @@ -30,8 +44,27 @@ export const plugins = [ strict: `ignore`, }, }, + { + resolve: `gatsby-remark-images`, + options: { + maxWidth: 832, + quality: 90, + }, + }, + // { + // resolve: require.resolve('./src/mdx-plugins/table-of-contents.ts'), + // }, + ], + remarkPlugins: [require(`remark-external-links`)], + plugins: [ + { + resolve: `gatsby-remark-autolink-headers`, + options: { + // icon source: https://joshwcomeau.com/ + icon: `<svg fill="none" height="24" width="24" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle;"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>`, + }, + }, ], - plugins: [`gatsby-remark-autolink-headers`], }, }, { @@ -44,9 +77,10 @@ export const plugins = [ ], }, }, + `gatsby-plugin-styled-components`, `gatsby-plugin-react-helmet`, + `gatsby-plugin-catch-links`, `gatsby-transformer-sharp`, - `gatsby-plugin-sharp`, { resolve: `gatsby-plugin-manifest`, options: { @@ -59,6 +93,47 @@ export const plugins = [ icon: `src/assets/logo-square.png`, // This path is relative to the root of the site. }, }, + { + resolve: `gatsby-plugin-google-analytics`, + options: { + // The property ID; the tracking code won't be generated without it + trackingId: 'UA-55628264-7', + // Defines where to place the tracking script - `true` in the head and `false` in the body + head: false, + // Setting this parameter is optional + // anonymize: true, + // Setting this parameter is also optional + // respectDNT: true, + // Avoids sending pageview hits from custom paths + // exclude: ["/preview/**", "/do-not-track/me/too/"], + // Delays sending pageview hits on route update (in milliseconds) + pageTransitionDelay: 100, + // Enables Google Optimize using your container Id + // optimizeId: "YOUR_GOOGLE_OPTIMIZE_TRACKING_ID", + // Enables Google Optimize Experiment ID + // experimentId: "YOUR_GOOGLE_EXPERIMENT_ID", + // Set Variation ID. 0 for original 1,2,3.... + // variationId: "YOUR_GOOGLE_OPTIMIZE_VARIATION_ID", + // Defers execution of google analytics script after page load + // defer: false, + // Any additional optional fields + // sampleRate: 5, + // siteSpeedSampleRate: 10, + // cookieDomain: "example.com", + }, + }, + { + resolve: 'gatsby-plugin-sentry', + options: { + dsn: + 'https://2e28bddc353b46e7bead85347a099a04@o423042.ingest.sentry.io/5352677', + // Optional settings, see https://docs.sentry.io/clients/node/config/#optional-settings + environment: process.env.NODE_ENV, + enabled: (() => + ['production', 'stage'].indexOf(process.env.NODE_ENV) !== -1)(), + }, + }, + // 'gatsby-plugin-webpack-bundle-analyser-v2', // this (optional) plugin enables Progressive Web App + Offline functionality // To learn more, visit: https://gatsby.dev/offline // `gatsby-plugin-offline`, diff --git a/gatsby-node.ts b/gatsby-node.ts index 1e0875f..bfce91a 100644 --- a/gatsby-node.ts +++ b/gatsby-node.ts @@ -1,36 +1,61 @@ -import ModuleOrdering, { divisions } from './content/ordering'; +import { SECTIONS } from './content/ordering'; -exports.createPages = async ({ actions }) => { - const { createPage } = actions; +const mdastToString = require('mdast-util-to-string'); +const Slugger = require('github-slugger'); +const Problem = require('./src/models/problem').Problem; // needed to eval export - // Generate Module Pages // - const moduleTemplate = require.resolve(`./src/templates/moduleTemplate.tsx`); - Object.keys(ModuleOrdering).forEach(division => { - const processItem = item => { - if (typeof item === 'object') { - // this is a nested module - item.items.forEach(x => processItem(x)); - } else { - createPage({ - path: `/${division}/${item}`, - component: moduleTemplate, - context: { - // additional data can be passed via context - id: item, - division: division, - }, - }); +exports.onCreateNode = ({ node, actions, getNode }) => { + const { createNodeField } = actions; + if (node.internal.type === 'Mdx') { + const ordering = require('./content/ordering'); + createNodeField({ + name: 'division', + node, + value: ordering.moduleIDToSectionMap[node.frontmatter.id], + }); + } +}; + +exports.createPages = async ({ graphql, actions, reporter }) => { + const { createPage } = actions; + const result = await graphql(` + query { + allMdx { + edges { + node { + frontmatter { + title + id + } + fields { + division + } + } + } } - }; - ModuleOrdering[division].forEach(item => processItem(item)); + } + `); + if (result.errors) { + reporter.panicOnBuild('🚨 ERROR: Loading "createPages" query'); + } + const moduleTemplate = require.resolve(`./src/templates/moduleTemplate.tsx`); + const modules = result.data.allMdx.edges; + modules.forEach(({ node }) => { + if (!node.fields.division) return; + createPage({ + path: `/${node.fields.division}/${node.frontmatter.id}`, + component: moduleTemplate, + context: { + id: node.frontmatter.id, + }, + }); }); - // End Generate Module Pages // // Generate Syllabus Pages // const syllabusTemplate = require.resolve( `./src/templates/syllabusTemplate.tsx` ); - divisions.forEach(division => { + SECTIONS.forEach(division => { createPage({ path: `/${division}`, component: syllabusTemplate, @@ -48,6 +73,115 @@ exports.createSchemaCustomization = ({ actions }) => { type MdxFrontmatter implements Node { prerequisites: [String] } + + type Heading { + depth: Int + value: String + slug: String + } + + type TableOfContents { + cpp: [Heading] + java: [Heading] + py: [Heading] + } + + type Problem { + source: String! + name: String! + id: String! + difficulty: String + starred: Boolean + tags: [String] + sketch: String + url: String + isIntro: Boolean + uniqueID: String + } `; createTypes(typeDefs); }; + +exports.createResolvers = ({ createResolvers }) => { + const resolvers = { + Mdx: { + toc: { + type: `TableOfContents`, + async resolve(source, args, context, info) { + const { resolve } = info.schema.getType('Mdx').getFields().mdxAST; + let mdast = await resolve(source, args, context, { + fieldName: 'mdast', + }); + let cpp = [], + java = [], + py = []; + // lol the spaghetti code going to be insane + let cppCt = 0, + javaCt = 0, + pyCt = 0; + const slugger = new Slugger(); + mdast.children.forEach(node => { + if (node.type === 'jsx') { + let str = 'exact match ' + node.value; + cppCt += str.split('<CPPSection>').length - 1; + javaCt += str.split('<JavaSection>').length - 1; + pyCt += str.split('<PySection>').length - 1; + cppCt -= str.split('</CPPSection>').length - 1; + javaCt -= str.split('</JavaSection>').length - 1; + pyCt -= str.split('</PySection>').length - 1; + } + if (node.type === 'heading') { + let val = { + depth: node.depth, + value: mdastToString(node), + slug: slugger.slug(mdastToString(node)), + }; + if (cppCt === 0 && javaCt === 0 && pyCt === 0) { + cpp.push(val); + java.push(val); + py.push(val); + } else if (cppCt === 1 && javaCt === 0 && pyCt === 0) { + cpp.push(val); + } else if (cppCt === 0 && javaCt === 1 && pyCt === 0) { + java.push(val); + } else if (cppCt === 0 && javaCt === 0 && pyCt === 1) { + py.push(val); + } else { + throw 'Generating table of contents ran into a weird error. CPP/Java/Py Section tags mismatched?'; + } + } + }); + return { + cpp, + java, + py, + }; + }, + }, + problems: { + type: `[Problem]`, + async resolve(source, args, context, info) { + const { resolve } = info.schema.getType('Mdx').getFields().mdxAST; + let mdast = await resolve(source, args, context, { + fieldName: 'mdast', + }); + let problems = []; + mdast.children.forEach(node => { + if ( + node.type === 'export' && + node.value.includes('export const problems =') + ) { + let str = node.value.replace('export ', '') + '; problems'; + let res = eval(str); + Object.keys(res).forEach(k => { + problems.push(...res[k]); + }); + } + }); + return problems; + }, + }, + }, + }; + createResolvers(resolvers); +}; diff --git a/gatsby-ssr.js b/gatsby-ssr.js new file mode 100644 index 0000000..75d85b8 --- /dev/null +++ b/gatsby-ssr.js @@ -0,0 +1,6 @@ +// This file shouldn't be necessary, but for some reason Gatsby is not loading +// gatsby-ssr.tsx (note the "x") properly... +module.exports = require('./gatsby-ssr.tsx'); +// this line shouldn't be necessary, but for some reason Gatsby does not load +// gatsby-ssr.js without it... +exports.onRenderBody = () => {}; diff --git a/gatsby-ssr.tsx b/gatsby-ssr.tsx new file mode 100644 index 0000000..7f3c037 --- /dev/null +++ b/gatsby-ssr.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +import MDXProvider from './src/components/markdown/MDXProvider'; +import { UserDataProvider } from './src/context/UserDataContext'; + +export const wrapRootElement = ({ element }) => ( + <MDXProvider> + <UserDataProvider>{element}</UserDataProvider> + </MDXProvider> +); diff --git a/package.json b/package.json index c774ee5..f08df9a 100644 --- a/package.json +++ b/package.json @@ -5,26 +5,36 @@ "version": "0.1.0", "author": "Nathan Wang <nathan.r.wang@gmail.com>", "dependencies": { + "@babel/core": "^7.10.5", "@mapbox/rehype-prism": "^0.5.0", "@mdx-js/mdx": "^1.6.6", "@mdx-js/react": "^1.6.6", "@tailwindcss/ui": "^0.3.0", + "@tippyjs/react": "^4.1.0", + "babel-plugin-styled-components": "^1.10.7", + "classnames": "^2.2.6", "esm": "^3.2.25", "gatsby": "^2.23.11", "gatsby-image": "^2.4.9", + "gatsby-plugin-catch-links": "^2.3.11", + "gatsby-plugin-google-analytics": "^2.3.10", "gatsby-plugin-manifest": "^2.4.14", "gatsby-plugin-mdx": "^1.2.18", "gatsby-plugin-offline": "^3.2.13", "gatsby-plugin-postcss": "^2.3.6", "gatsby-plugin-react-helmet": "^3.3.6", + "gatsby-plugin-sentry": "^1.0.1", "gatsby-plugin-sharp": "^2.6.14", + "gatsby-plugin-styled-components": "^3.3.10", "gatsby-remark-autolink-headers": "^2.3.7", "gatsby-remark-custom-blocks": "^2.3.6", + "gatsby-remark-images": "^3.3.14", "gatsby-remark-katex": "^3.3.6", "gatsby-remark-prismjs": "^3.5.6", "gatsby-source-filesystem": "^2.3.14", "gatsby-transformer-remark": "^2.8.19", "gatsby-transformer-sharp": "^2.5.7", + "github-slugger": "^1.3.0", "katex": "^0.11.1", "prism-react-renderer": "^1.1.1", "prismjs": "^1.20.0", @@ -33,13 +43,19 @@ "react-dom": "^16.13.1", "react-helmet": "^6.1.0", "react-transition-group": "^4.4.1", + "recharts": "^1.8.5", "rehype-react": "^6.0.0", "remark": "^12.0.0", + "remark-external-links": "^6.1.0", + "styled-components": "^5.1.1", "tailwindcss": "^1.4.6", + "tippy.js": "^6.2.4", "ts-node": "^8.10.2", + "twin.macro": "^1.5.0", "typescript": "^3.9.5" }, "devDependencies": { + "gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.10", "husky": "^4.2.5", "lint-staged": "^10.2.11", "prettier": "^2.0.5" diff --git a/src/assets/logo.png b/src/assets/logo.png Binary files differnew file mode 100644 index 0000000..afc4610 --- /dev/null +++ b/src/assets/logo.png diff --git a/src/assets/logo.svg b/src/assets/logo.svg index 019162e..50eb140 100644 --- a/src/assets/logo.svg +++ b/src/assets/logo.svg @@ -1 +1,13 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="354" viewBox="0 0 1280 354"><defs><image id="img1" width="1280" height="720" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQAQMAAAD1s08VAAAAAXNSR0IB2cksfwAAAANQTFRF////p8QbyAAAAIdJREFUeJztwTEBAAAAwqD1T20JT6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeBrE3wABB8CymgAAAABJRU5ErkJggg=="/><image id="img2" width="240" height="242" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADyCAIAAAD8/9/OAAAAAXNSR0IB2cksfwAAO+VJREFUeJztnfdfE9n6x7//gm33urquq9t37+71ith7772CXewVO7a1Y6+odJAuHQREmvQmXaoUpRM6hBaS8P2QuFzEzGQymWSSwOd1frBM5rT3nPOc9pz/6+gTqYTCjrY2fmMTj1PdnFdYl/CO8yr8o5NPjqHx22OGEbpH/eds9RizwvHXuTYjplsNnmA2UNuk32hjigEP4yf4IX6Ol+BVeCFei5cjCkSE6PI+1iFqJADJQGL6RK7/YzsBqqt2vqC6riU7vzYkpsjxZc5ts4R9F98s3fNywhrn/yy2/36a1TcTzQdqm1LHlxripngtXo4oENHS3d77/n5zyywBCUAykBgkCQlju2xUV31Af6aWVn4ph5ucWXnfKmnP+RDwNGmd878X2n0/3eqrsQyzSz0gaiQAyUBikCQkDMlDIpFUJJjtMlMt9QH9SejTQ+OKLxvF7TAIXLzTCzbA1+PM2CKYPCBhSB4SiaQiwUg2Es92+amKeinQvHYBDNPEdI7769wDF9/M3uz+3VRL1kmlHZB4ZAEZQXaQKWQNGWS7jNlRrwOazxdWVDVFJ5XdNk9cdcBXe6XT4AnmrBPJSEBGkB1kCllDBpFNZJbt8la2egvQQmEHt4nnF/rh4sPYJbu8Ry11+Nd4FbUo5A/IGjKIbCKzyDIy3numR3oF0M2t7QERhVeM4qZscBk5w7q/lgwza+obkE1kFlmGnf06orC5pZ3telCGNBlogUBYWd1y7l70xLXOA3oHxCRwT1jrfPZeNMxrFAvbNaNAaSDQ6F6r61pehX08cCl0mq7bIKan2wDHV2NNh0+z/EW0GjJzk9uag347zwQduhx64UGMofFbI5sUS5cMO68sJ58cV/9c76B8nzcFCPgD/op/xH/hATyGh/ET/HDn2aC1h/zwKrwQr8XLEQXjPQmKYpqO6/6Lb/zCPqCINNIO0Sig29sFJRXc5x6Zy/a8/H2+7cAxzAAxYIzJkInmP8+xmarjuu104B3zJDuv7OCYoqjEspSsyvcf6ko53Jq61gZuW2sbv51PtQEUitZu8BP8ED8v5TThVXghXouXIwpEhOgQKaJGAgYwlB0UCwoHRfTcPRPF1a5Z8yGaAzRAcn+du+/vEDRy/bWYadIwuoINeuRq2H2rJLSsoXElWXk1aNuUsJyBKBARokOkiBoJQDKQGKbGsigiFBSKC4VG+RtUA2kC0GjkAiMLt5wMYMRQBjGT1znfMEnwDf2ApleoSh0zElPGafIL/XDDNGHKepfBExiAG4WGokMBohjZzh8DUm+gq2pavILydxgE/TrXZhDdbRWwVkfOtJ6u63bg4htL14y41IqiskYV74hhqxSXNyKpVq4ZSDYSjyzQXpxH0aEAUYwoTBQp25mTS+oKdG19a1BUoe5Rf9iX9AZPMCUx/Jq/3dPcOT00rjinoLa+sY3tbNERko3EIwvICLKDTNEbPKAYUZgoUhQsipftbNGUWgJd19B60yRh7lYPegZl/9HGw6dZ7j4fjIEXRmAasxECGUF2kClkDRnsT9fiQsGieFHIbGeIjtQM6OaWdguXjDlbPej1rd9MMJuu62rhko7xFttZUayQQUuXDGT2m4k0F/ZRyChqtVuOURug23j8pHSO3pmgETOsZJ3AGtzJsdv+i2+CY4oKSxtU3D5mSshmYWljSGzRgUtvZmx0+0bGLSsoZBQ1CjwxndOmPuNFNQBaIOgc2t8wThi7ykkmlPtrGaN92nY60NYzKzOvRn3tQjlV29CalV9j55W1zSAQBSLTnCYKHMVuaPy2lMNViyVGNQA68R3H4E7Ub/NsZWpgBo01Rad55UlcRm51k7r1m4oQCiEjrwYFgmKRdfUUhX/6duTbdxVsZ0K6VBpobjMPhuCwKbLtVO7ct7DmhfvrXM2YWGVcKBYUDopI1tmhYVMszJ3Tuao9hlZRoJua22NTynWP+svUP/4y12bNQT8bj8xeu72dulBEKCgUFwpNhsZitLHOUX9UDbdZRTs9VQQaTYirf+7Go/7UZ+W+Hmf636UOMPWSMir7GmaKQkGhuFBoKDrq583+Nc4MDQ0qSDXLWbWAFgiFeYX1Jo5pP8ykumsZj41a4nD2bnRoXHEvmb5gVii00LiSs/eigTXFvQMoc1QQqgmVJVClrQEdqgZ0flH92kN+QydZUGwtBmmbrNzvG59a0XeyX07x+cK3aZzVB3wHUfYrgmpCZaHK2E77Z1IVoNt4fJ83BVM3uFIfoMzb5uERkNeHMoNq5ws9A/Pnb/ekPhBHlaHiUH1sp/2TVAJoDAEDo4rGrXpBpcvDMPH7aVZ7L4QEaMoGMZWSeOvivr9Dvp9uRWVEjipDxaH6mlRjmMg+0EVljc8c0mDAURr8jTdbtuflE7vUXrtKohzVNbQ+tU9FUX9NbVyO6kMloirZTjjbQOOzPnwllPq+Xr0zQSUVXBUbh2imUMgoar2zQRSrBpWIqmS9nWYNaIxC4lLLdxgEUenXBmqbTFrnYuL0rhc6mmBXfIHQ1Ond5PUuVJxQoipRoahWFquJHaDx9ae/r9Y96k9l8IHR9HRdNwfvbNUZefQq8Xh8x5c5Mza6UZl9QoWiWlG5bPWiLAAtEAiT0jkr9/tQ+ei/nWLx98OYxHROX9vMolD4SRmVFx/FDpsinWlU68p9PqgyVjYzsQB0WnbVlA0uVMyyH2c9f2qfpklHONVaANTY8R0qhUrdwUpJzapSfiKVCnRLa3tMchnsB6nFMWSi+fztniExxcpMXp+o6E1sMapmCIVzA9N0XaOTyppblTpMVB7QQqEwIqF0++lAqWc5B4wx0Tnq7x1c0LfHSAWFSkHVoIKk7k1HRW87FRD+tkSZJ+eVBDR6q8y8mlmb3KUunQyfarn6gG9RWaNabCfvnULVoIJQTcOl+SDur2WC0WRmbrXS7EYlAV1QVH/ZKE7q7jk8gG86IKKwj2YVFyoI1YTKolKnlx7HKm3LhzKATs6snL3FXeopie+nWz22Tek7XaJGQmWhylBx5DU7SNt09mb35IxKJSRJ4UDX1rfqXwsfLO07/m6q5anbkTWafhhb84QqO307Uur9BwBA/1qYEjYsKBZoTnXzmbtRX48ja5v7axn/NOu5ofHbvp1GaipU3A2Ttz/Nfk6+hR0YAAYgodDEKBDohsY2S9cM5JP82/11ng3a5oJi1dpW2yeZ9KG43uBOFKqSvK4BA5BoUKSHKkUBXV7ZtOts8HekK9sDxpiMWeH4JrZYY5ZOWlrb0QKlZFa6vc698CBm0/HXC/W8xq168d+lDqOWOmivdJqz1WPdYb8TNyJsPbPiUspLKrga47cJlRgeXyLV1QSQABjAQ0HJUAjQQmGHvXf2b/NsSTYe9R9t/Pt825umCS3KnXhXkJDlBi7PP/zjXcskIAuCh02xlHgtp9jb9B8L7Jbufonhv7Pf+6raFs1Y2EdV3jZPRNZIvJABCYABPBQ0N8080AKBMDal/K/F9uTb6H6bb2vnlY26ZDwByhdaZQuXjG2nA/GJyuR666txpj/Osl59wBdDiLxCTTC6qmtbHLyzf59vRz5qAh6ARBGTs8wDXVLeqHvsNbmnwIFjTI4ZhoNmDdjZXFHVdPxGxOhljrRdkQ/U7nT7uf10YGp2lbpPwIvvAzlxM4J85xnwACRAhfEEMAk0MlNY2rjlRAB5/f0wy/qJXaq6z2m0tws+ljSINqAxdmPnIG3TXeeCU7Oq1N0Ma+PxnzmkSd3GBFQADLONGpNA1ze2wTb6iTQbqDO9s0GlHC6D8bKinIJaDAB+lcVLC5Xw3VTLQ1dCoxLL2M6fvCrnNOHjJF9NAyoAhlm33EwCbe2Wob3CiSQD6JSX7PLOyK1hMFLlCy1KRXXz4l3eCrpN+etxZuNXv0jPqVZ3NyOZeTXL9rwkP18HYIANg5EyBnRTS/vsLe7kvuOn6rh6BOTxeOpdT6WcpofWybTvf6ASYGLqXw+HSc12XuUSr13gFZQ/XZfMNQWAmb3ZncH9DswAjV7DyDaVvJKGTbEIVv/9zTV1rYeuhEndvSB/6OzNdnurmhsXGnoTWyx1mPHYNqWOIcODAaAxvLN0zZiwxpkkxT/MtD51O0rdDwUKhEKzF+9GUHNYwQjTp29HqfvouY0nMLgT9cMsa5KcTljzwsIlg5GcMgB0fGr5hLXOJF0wKubq0/gPxQ3yx8WuEtI5FP2HMBWGT7W0cs1Q98lNVP21p/Ek05oYO45f8yIutVz+uOQFup0vuPAwhtx35XRdN5iD6r4YBotw++lApm6npR4wQKyoUuyGHkULVZ+WXUV+9A4InbsfLf8ZJXmBtnTJGDqZ7CQwWu6QmGJ1b2MEAqFf6Aclo9wVRBsE1NvwAADAgHwkPXSSBSw6OSOSC+icgto/FpDdFPHtZItLj2PVffqpo3M5sHmztAUjxQW0bVn5NereKAADwPAtafP3+wLb7Hy5ZnXpA41h6ZFrYeSdyA6DIA1wQsfjCdC6/E766So0DJti+cw+TQP2vQAGIEFuoB6+GlbXQH/GgybQ6IJjkstHkY6Q5m3z8AkpoJ0y1VF5ZdMtswTaF/7JH2C4bzr+Ov19NdslwYCABMAgyeyoJQ5Ai/aeFppAw8bHp0aSrB9mWgdEFmqG82aU78xN0n2JKDQMn2blEZDPdkkwICABMIAHSWZ3GASm0V1UogO0QCj8+2HMCNLFhWOG4Wp6dfaXsvfKJrf8lBD6axmfvBnBdkkwI4ABPEgyC7QAGL3LLugAnV1QSz4d+9ci+7zCOnUfxHTp0JVQ5aykkIcZG93U7qJiiQIYwOOvxfYkmYU1m51fS+PlMgPdyuMb2aWS2PXfTbO8a5Gk7vt6u4QR4UI9T9Zp7ic6ElGm/rsUxQIe9yyThk8jXBL/apzpY5sUGmuHMgMdElNEMkOOlmzDkVfqvv2ou2rqWv9D2pYoLQydZJGcqQzXFsoRr12go092D+U0HdfAqEJZ20XZgOZUN6877Eeyhokhamic2u9A6q6C4vqRpCMYpYVB2qZeQZowLuxSWHwJie0KK2DtIT9ZV0llAzr8bcm/FxIeFxs8wXzLyYBqzXIWk5lXw+CZFHnCgDEmNh5ZbJcHk0Lvt+1U4DcTCOdDf59vC+hleqcMQMOgOW4YQdJHLNTzCo2TLXrVV2pWJYsz0D3CM/s0tsuDYYXHlyza6UWUX8B27Hq4TJa0DEAnpnPI74U2c07XjInn7krJquoDWnECMObO6SRZ/mXO84R0DvUXUgW6vrFt6ynCzQzoDceuclL3nbsSlZVfo6CjVrIGFLKdVzbb5cG8gM04Uvc0W04EUF/ToAp0ZEIpiaOnEdOtzt2LppsjldbHkgbyzelKC1+NNfUNLWC7PBQiwDNiOmEhAzzgR/FVlIBua+OfvBVJZD0P0Op0uJ/wToZ+QY1UW9+q5E39ROHbyRYw6NkuD4Uo8R1Hl/hKANEqaWQbtf5fOtBCYUd2fs2kdYQnrEbOsHL0yWngaoiPth6CkTd/u2osrMyzLVOYSzh21djEe+GbQzI9OnGtc1YepQ200oFGE/XMIY1kbmXVAR9F+0hlV+cfxFC5hFzRYfFOL01aseqhyurm1Qd8ifI+eIL5U/s0KluRpQOdml2F7oDI3oBhd88ySWPch0oUhmJU7udTaEB3jG6X7ZJQoIDQfStC5xDAT0ffP4WCxSUFaKFQ+MgmmWTeat42j4+lan/6lVzxqRWwOtjdn/TznOeegXlsl4RiVVTWuGAH4Zz04PHmD6ySpV6oJQXo+sa25XtfkhS066tcZV7axYpguRoavx1MbHQpvHnGsFv/1bsc9fY7I1XgyP11Hkk5LNn9srZBitUhBWivoHyS5nnSOpfecJWgQChMTOeMXu7IFtA/zLJ29c/lNmvmsLu7gNPk9YS3DKNN8QiQ0k2RAd3GE+gTnxocNNb04qNYRrOjuqqoat54/DVbQGvGIVmKAlQkLh4PXw1rIx0ZkwGdnFlJshUJ/xVBebpb3cXnCwMjCxXqz44kPHqeou4OdqkLUJFQ98dCuyTS6+HIgLZ2zyS6wApW3aKdXmUczZwWlaja+tbZW9yVT/PwaZaZnVOwvaN9Fo1YFu30JponBZDAkuTnhEDX1LWu2u9DVMqjltibOGraRhmpKq9qmrvVQ5nTHX8ssGPEQZZ6ydTp3aglhKuzwBJwEv1WMtAYBsHeGLOCcBi05qBfbEqvK2g0k8YOqd9ONiO/cIOpMEjbWP9aqLp7uKShuNSKtYf8iIoFWAJOoiO0koFubOLZeGQNnUS4mvDUPrW511h13RUdlzplmeHgsQ8VTvMYo99nGr5wD+o9xkaXMGAwdkgjKhlgCTiJrsOTDPTH0oaTtyKJHBN+M9E8Kqms95Vzh0AgMDe3WLp6139nnVU00L9MvTpn2b6Ll640NfWigYpYQCsmuWwIQXsKLAEn0XKeZKBf+L0n2Smy+oBvL7xiHjQHBgZu2rRpvUhacwyGjr/ff/QzxlH+19hHv0+/tGadLmLZsGHD48eP29o0xMMJdTW3tJNYHYATiEr8oQSg8X1cfRJPvJfP5Nz9aHpOQNRaDQ0Np0+fXv+Plq3aOn7e8W/GPWCW5q+1H4+adW7B8p1dEW3btq2goKC3GR7I7oUHMUSXhwNOICqxSCQAzW3mLdtDuNz9+3xb72CNOntMRTwez9fXd/3nWrNWd9aSfT9Ovt5fi5l2etiEu1MWHFm1ZlOPiIyNjRsbmb/ST8XlE1LwxwLCCWkgKnHpVALQReWNfy6S/KLO+xKP+muG10CZVFlZee3atfVfaO06nQXLd/0+/eLAMU/kQXmA1jN8GLOW7F+9duOXsRw9ejQ3Nxc2D9vFoFQBs43H/IkmlIBokaR7O3sCjWb8ZXAB0Vu+nWzh5JOjGQ6pqIvD4djZ2W3evPlL1LpbIFMXHh4x6SaMEIpwD9B6Onjsw+8m3IHpsnC53rp1G4herqOjY2ho+P69ZKtRUwXMABuRV0EgClC/tDp6At3axn9onUxUB7/Nt414W6oxbr4oKiEh4cSJExifkQDdaYGs05m7dM/E+cd+m3b52/F3YQ0P0u609rq3DjAKB44x/krbaMi4+z9PuTp27slZiyW3yj2kp6fn4+PTqyxpYAbYgBwRjQD1y3PZPYHOzK0hOd29cIdXRVXvmkVqbW29ceMG2kipzIm1bv0GGMGLV+zQ1btg9SLe2i3T0Pjt3w9jMcTBOMbsxTtb99Q9+ncXrtBbsXozSav8pWB4lJf3rsUswLaQeIc0QM384hLXnkC/DCkguSlxz/mQ3rBftLvS0tLQOlLHrktGRkZ1dXUtre2VNc1llU2lnCZOVTPGMc3NrZ6enjReuHHjRmdn517VSAO23edDiGgEqF961O8JNJoTkjvHTRzlvdNFvcTlcvft20cDPtgnHz9+JHptc3Pz3r17abwWHUVBQYESC4B9ATkiGn+cZQ1cezz/GdD4IPTOBhFtkvxhpnV0ktpfqk5dbW1tr169kmo6fyldXV0bGxs+n2wPRlxc3Pbt22kwbWpq2qum8IAckbt/gKp3JqiHyfAZ0MXljXOId0jO2eqhATcAURR6djSxBgYGNJg7c+YMjA3y9/N4vGfPntH4Wvbv349Bau9ZO6xtaJ27lfBOltlb3IvKPvu8PwM6MYMzagmhL2QY0Jp9uru7Ghoa0DzTaERh6Xp4eFCxdGNjY2lY53i/iYlJaWmvOVohEO69QGhG/2exfQ8PR58B7RGQN4zAj1t/LRNbT43y5UquxMREtIWy0gZj49y5cxRpg+Xw5MmTbdu20WD65cuXii4B1ZGdVxbRGviwKZZu/p+dMvwM6Me2KUQGNAzw+LQK5WaENcEeuHXrFg174MiRI2jX29spLTyhFU9JSbl06ZKssawXTeHV1tK5gkQdhTb4p9mSJyqA68PnKd0f/gzow1fDiI5jzN3mWVmjUZ7MSZScnEy+LkgkR0fH6moZ9gW0trZGRkbSiEhHR8fLywsfnuIKQXVUVduyYIdkb2zA9eCl0O4Pfwb0cuI9SdsNAlvbesUMNKxnc3NzepC9f/9e1h0XGD7u2bOHRnTXrl3rJbvw2ngCvTOEl2Iu2eXd/eH/Ac3nC0n8xl59Gq/0jLAgtHlo+WhYz7BPHj58SC9SxLh161ZZY8RPHj16hM+P2RJQTV1/9paIzJEzrbu72f8f0AXF9UQG9EBtU2s3sqO2GqMPHz4YGBhQX+ju0tmzZ5OSkuhFWlRU9PTpUxqR4sN7+7bnyoJG6rlH1iBtyXACWqDb9eT/gPYOzif6CEbMsA6OLmIjI8qWhYUFDet57969MTExLS00xxiwUmA8dD89QFH4Bi5fvtwbLOmQmKKRMwjNh+6Xg30Cmi8QPnxOuMnuv0sdNOmGPCJVVVUdOnRIVqqgW7du1dT03CUjkzA6tLW1pRE1DI/ExESNt6RTMiv/u4zQsQHQ7Voh+QR0c0u7/jXJ1y8P0DKZu82jx3qM5onP56Pfp4EUFBsbKz9SFRUVNCzp9aIpPI23pIHfvG0eRN5ngG7XHv1PQFfVthAdu4KNoqPvr/GL3vHx8V0HYKlLV1f33r17TJ0l8fX1pbHOArm6ulKc/FZTAT9ASDTGA7oAWPzkJ6CLy7njV7+Q+PQ3E813nQvWbNeXzc3NRkZGNEjCsCwlJUV6BNQEm+f8+fM0FnT+/vtvNPAafEYL+AFCIke4QBcAi5/8BHR6bjXRNUTDp1mduh2pwf57MJiDzUBjmyhadGtra2YxCgwMpLHBA7aKi4sLvgcGU6JSAn6AEChKnrSYbg2AxU9+AjoqsZRoWuSXuTa3zRI1eFtSTk4OWjga7SKM17w8hr3q19bWXrp0iUZi8BmEhYUxmxjVEfADhET3vgJdACx+8hPQJHN2WssdHbw18L5HsXg8nrGxMQ2Adu/e/eLFC/JNz/QUExNDYwoPunDhggaPDgGhFrHP+S7XGp+AdvTJJnp06gbXwMhC9jKiWJWUlNCeqlPQ4ZH6+nraU3gw6BXxjamCACFQJKIUAIsf6wRaKOx0YEr06IIdnlRuH1JH1dXVOTs70zuTkpiYqLhBWHl5Ob2DjA8ePMjP10w3QICQaIsSAgAWT5x2As3nC+9aJBI9umq/74dizezIgoODYQfLCg0+AJgEil7LePbsGY01y+3bt9vY2GjkdAcgBIpElAJgYNwhBrq1jf/3w1iiRzcdf12jiZPQHA4HRieN5vnEiRORkQq/MjA3N5fenuyDBw9mZGQoOnnKFyDcRHzNDQAW++joBLq5pf3EzQiiR3eeDf7SnYcGKDw8nEa3vmnTJisrKyX4x2hsbAwICKB3RsvJyUnRyVO+ACFQJKIUAIsXCzuBrmto2/c34bEt/avhbOeFeTU3N9Nbwjh58mReXp4SBl4waWpqam7cuCFrCteLZmCKi4sVnULlS/8q4Z1sABgYd4iBrq5r2WFAuIHa4E4U2xlhWDAxg4KCaIAC2dvbK9NCjYmJwQCURjofPXqkeZ7SgSIRpQAYGHeIga6sadl6ktD914UHMWxnhGFlZ2fTOyRy/PhxmU5Yya+2trYnT57QSKqOjk5oaKiGTeEBRSJKAbD4iGAn0Jzq5o3HCM3tS4816nbN1tZWFxcXGsaG+Ky18icQ3r9/T28K7+HDhxp2kBYoElEKgIFxhxjossqm9UdeET16XYMOXwHHnJwcGlN1aPAuXrzI1lZ6CwsLGjsB8RlERUVpkksaoEhEKQAGxh1ioEs53DUHCWf4bpomsJ0RxtTQ0ODt7U1jfnfbtm0susJITEyksXcKH6GRkVFRkeYcNQKKRJQCYGDcIQa6uLxx+V7COzbvWdI8KqeCAs309rI9ePBAqncvxQlmkqurKw2mYSbBBNeY8yxAkYhSAFwscugvBpq7Yh8h0HctEtnOCDNC50tvV93JkycjIyNZHGCByIKCAnpTeLt27aqp0ZCblUnWswGweEu0yIbmNK09THiF1pceS9VRoBlQ0qAZHTeMDdYtUVj/KSkp9M6zODg4yHnkUUUEFIkoBcDim+c7gS6vbNqgTzgovPpEEwaFhYWFMBto0LB//34VWaTgcrnXr1+nkYUzZ87Ex8drQCMNFIkoBcDlXYPCyprmzSc0eR4a5u+jR49oNG87duzIylIhF5Vg+tixY7L2M7q6umfPnsXoUN2ZJpmHBsDAuEMMdFVty/bTgUSPnr0XzXZG5BI668zMzEOHDsnqyQXPX7t2TdW8XtjY2NDbhRcWFkbbc4iKCCgSUQqAxedkO4GurW/ddY5w28fxGxFsZ0Qu0fZLtG/fPhX0SyR2SUMjO+fOnUtOTmY7+XIJKBJRCoDFjgk6gW7gth28HEr06IFLoWrdUwUHB9PwVbdp0yZDQ0MVPHba3NyMRnrXrl2y5mjLli3W1tZsJ5++ACFQJKIUAAPjDjHQLa38M3cJt31sOxXIbVKtbpe6YDAcPnxY1rpfLxpIpaWlqabRWV1dfevWLRqZwlfK4XCkR6CSAoRAkYhSAAyMO8RA89oFJBMi6zonRLhsZ4eOWltbo6KiaEzV4SdeXl6sT9URCZ9ZUlISPQ/WTk5OajqFBwjXkU4ui28P6gRaIBQa2aYSPbpkl3dWvloWAZrYy5cv06j1I0eOqPi2HnxsN2/epJE1fX39169fq+MuPEAIFIkoBcAC4T9HsCBL1wyiR+du9YhPVb/LKDCit7Ozo2Fr6urqMu4+RhEKCgqikTu06/gSVHBsIFWAkOQ6LAAsfuwT0K7+uUSPTljj3N1dqbooMzOTxqZnGBs3btxQi8stYUnDfqBheMCSVkeXNIAQKBJRCoDFj30COii6iOiioX8vtHvqkKqawyMitbe303Mfs2/fvvj4eBjfbOdAumh7lV4vmsKrr6+XHofKCPgBQqAoEVGgG/SP//JPQCdlcIZOtpD49MiZ1hcfxbar1RXfCQkJO3bsoFHTJiYmauR8CJa0r68vjWxu3LhRvbwdAD9AOJLgSlmgC4DFT34C+v2HOiLHYcOmWOhfD2tRn4PfaF+vXr1Kb3JDoe5jGBfarbKyMnpXLMMeq6hQm6ER8AOEQFEiokAXAIuf/AR0eVXTrM2SL0X+13izrScDxLPWqi+M32Eg0qhg0Pz48WP1sqzEioqKQotLI8vPnz9Xl8Vw4AcIgaJERIEuABY/+QnohsY2ou0cA7VNlu15WVHVzF52qAo4lpeXX7lyhUbtHjlyBK0d2zmgI0BpaGhII8v6+voYOquFp3TgBwiBokREgS4AFj/5CWheu+CyUZxki3u08cS1zqozFS0QCJtb20sruNkFtXEp5aFxxW9iiyMTShPecdKzS52cvbduldl6xsDfx8dHHZtnsd6+fbt7925Zc62ru+n+g2eJKbnJmZVRSWUoRoTopLL099WFpY3cJl47X1UKJDu/FhD2J5jiALq8f8Z4n4BGVVq5EU5F/zbPNiZZ4b6CqAgF7fAy+8qT+O0GgasP+s7f7jljo9t0XdfZm90X6XmtPuA5R8d43LzT85btXrOWqjsLHR2do0eP5ubmsp05+qqsrLxx4wb1Kbx16zYsWrFjwvzjk5ffXrXXdcku7zlbPabrdpbk3K0eK/b5bD4RcO5etOmLdxm51arwncemlP8+35ZkErorjf+71i0xnUN0LzJsFzd/NusbrYV7QN7qA75ElxJ8GYaMuz96tgHIllq7Bw8e9PPzU4uel0Rit+1SM7tk5fYJ8459N+FO/9HPqBTjV2NN0WQYO76rrG1hkWv313mDJ0g2oAEtOueuJ/8HdG1969BJknHBbx7bMHaTCHXx+UI0yXctklYd8B0x3YroeyMKA7Se/mvsw1Gzzs1csn/1WsnDJowFrays1GiqjkgYDb9+/ZpodLh2nQ6+be25p/CdDxzzRKZi7CeaF5u9xf3M3ai07Oo2Nua7jGxTiWp/yCTzmrr/rRv8D2jYpqOWEl4Fd+ZetEC5XQ9a5fD4klO3o36c9VxWlLsH1N/3k25OWnB04QoJ57319PTUfZdwl0pKSiS6HFm6atuMxQd/nnr1a+3HtIuxE52J5ttOB3oHF1TXtSgTBtg85+4Tbu3/z2L77velfHZ5/RZih2BrDvk1cpW3iTQ7v+aOReJfiyWvDNHBWusp+tm5y/as7mZbYyxoZGSk7sZGd7m7u3df8IetvHjF9h+nGH4lH8rdw8gZ1scNIzAKV1qm0LSR7LPbeOx194c/A/ra0/gBYyTPjIxe5pj7UUmOKRq4PIzwBhHM0cjVxox7MG7eCfS/4vqG0cngpWyqoLKyMjMzs080r98wY/EB9E6MF2N/LZNRSxw+FDcoZ7yYX1g/ZoXk21UGaJlcMYrr/vBnQNt5ZQ8hGHV9Pd7sZXCBopPezhe8iS0mObErfxg0xkhrtgHa6e3bt0dERKjFtg3qAmHv378/fPgwaJ68QH/w2IeKK8lZm9xtPbO6rnBVnHxDCoiWVL6ZaI40dH/4M6ADIgt/IFgu7yfyOKPo7zGvqB5mD9F1dEyFwWMfTFl4+PyFi0rwW658cbncp0+fzlu+a/jE2wotRkA2e7N7TJLCV6NIHCYB19fhH7s//BnQ2fm1JDv01h1+pdAdHdxm3qErYV+Pk/wtMhu+0ja6ZeTTxtMc67lLGK55+8cPHX9PCcUIA3Xp7peFirwHHsitP0zoNGb8mhdZeZ8t+X0GNKxvEq+NMKPzChVoRhs7pn01TvLln4oIsza75xWq0xZKiqqtbyXx9s146K9lrHPUn6ewzZhADuARxb76gG/j5wde/6/H78/cjRo2xVLij0Gbs997RSSazxeGxpUoYhRIXhMnbkYobaSrHNXUt5o4vftmAtXlJ6YCIhWfUWVcQI6omQOoX14v0RPoZw5pfy4inCy7/kwhfiqq61r0rxFen6G4oLXc0cJFoy6MCokpJvG7qbiwdM/L/MJ6RUxOAzmiSAEqcO3xfE+gIxNKl+1+SfSKDfqvGpl2adDS2u4f/vGPBYxNOVMPaKTnbPXQmEvr2tsFu84GE00IKDR8N9XSyDZV7IyLQQE2Eq+LAPXL6fCeQNc2tJL46Bi70in9PcOXjGAkuutcMNHOQEUHVH/XNdHqrqz8mp/nPGelGBGmrHdlfLUFsAE5ohgBKnDt8ZOeQKPTcHtNeGAWVourfy6z1pKdV/b30xU7T0ce8DlV16rHPncS8QXC22aJ8uwRkDMMGGMCwhi0OoAZYCMa0SEA1C9j6wl0h2jbHdGmtkFjTW+YvK2oZqxnQTUcuhzKYjUgTNN1DYgsVPJOFcZVWsHdfILw5iflhBkb3WqZs9+AGWADchLjAqIA9ctfSQC6qrZlynoXokTP3OgWlchYz1JU1jhqiT271fD9NCsMSesb1eOMmUTx+UJrt8zRywj3liknDJ1k8SaWMV/aUYllRMcC+3VaOC5VkvpVCUC3tfFJFp/RBfRYbJRH4W9LvyU4ba608PU40yW7vD+WqPEOUgyejt+IGMGq5dZPZHWYO6czlSk7ryyMNYniAqISN7JKABoCskQnbBF0j/ozdfv3HXPCWzOUGX6eY8Ngt6N8ZRfUTtNxHUiwsUyZYfe54JZWBtZfASvJVfWAk6hVlQx0THLZnC2Erb3WckfxjUNySiAQHjck9PirzDBkkvmrsA/y54gtJbzj/LWIZctNHFbs82Gkr8OQQJt4fgNwAlGJP5QMNKe6+ZZpwlcE9vggbROfkAL5R1EwW9HYs14H/UQT0jYemXJmh0WFxBSxO1PUFaZscMEIW87sCIVCv9APRMNBYAk4OQQzE5KBbmnlu73OHT6N0IIxNH4r/yiqoLh++V7CRRwlhwdWanxuxS/sA/XTlgoN6L3tvbPlzA7QumlCeMcmsAScRHPHkoGGSiq4C3d4Eb10wpoXDi/lTXd0EtkwVsnh9O1IObPDolDBRCczlBx+mWtz10Lem1odfXImriXc9QksASfRbwmB5rULjhmGE730uykWGFbLuccqLL5kxkY31utAHI5dD5cnL+zK5RXhWpiSw4+znt+Q72JLQHXiZgTJ/AawJAGPEGgIdgzRXAeMzinrXbLz5fIKrlIt9Km+FpqJ8MsceVtoQAW0iNbavp1i4fOGbPhOBjQa9snEKywjplt5BubJk/SUzMr52z1ZrwNxuPQ4Vp68sCuvoHyiEbySQ6fzZfueO+BkEqAimVCftM6FxN7oIAcasn+ZTbJ7a6GeVxuP/oR0bX2rjr4/63XQT9ThWLup9ywHyQhemQEt4KvPz0TJpDaeYPFOwnsngKKdtEU9KUBn5tVg3EoUwbeTLXocgJFJsIT2/h3Ceh3061wsNPMMVOM9dzFJZb8SeENWcliwwzM1m/59F7A3viVe0fvvUoeMXCmbPaUALbbQSTYPHboSSnv+TtjRccOEcPu2MsNPs59HvFXjlUI0K2gaVcGM3mEQ1NRMc6Wwgcs7QnrO45hhBJpw8pdIARp64fse9U0Ux+8L7N6+o+832947m/VqwOeqvdJJnnaFdZVyuGsO+RF5f1NmuPAghvaCW1IGh+jSiX6i+RNHnxypL5EONGxwpJJkn8Ce88G0t3bApEFC2a2DIRPNt/xz9bmaCh3pLdME1q2OQdqmHgE05wmAEIn9CfzO3Y8uLpd+X6Z0oNHIB0UX/TaPsLDQvMWmlEvtCySqpbWdlTNw3QMsM3Pn9Ha+2txEIVFJ6ZxFOwkXwpQT/lps/6GYzkYOwBOXWj521QuiN/86zyYwqpDKDIR0oDtE0xEbj/kTRYYRlcGdqA90t6RYuGSQzKIrIWw7FVjKaaKXeNURjye4+iSezRMrWib618P5tHykfyxpOHM36l/ELll0j/lTPDpACWiBUHjfKonk2N+MjW5+YTQna6KTOnf2sVUTsOAfPU8RCNT7uIpY/uEfh0xibXP5z7Of0/Zy8SrsI8maMcADfhQ3w1ECukN0tGTyOsJFlkHaJmsO+dLLTH1jm41nlqLdfxEF9JKMbIVVBaFHXqTHjtWBXvrkrUh69gYEeEi8sgC8IsrOmagCjTYMhibJlq4hojNeNNxR4hcFRfXL9rCw7Q6mzuk7kZrRPIsF+42Vg1hjVzrFJJfR2NuD2k/KqBxC4Gm/n+jsoNmLdOp1RBXoDtFZw1UHCB2FIey/+KawlM43ioLweVOg5MVb2HxIcJo6z9Z9qfKqpvuWSUp2zdF/tPE9yyRuMx2HLYWljQcuvSF5+ar9PlU1MpzJlwFotL4Pn6eQzN+NXuZo4ZxObxqyrqFt7WE/omuOFBF+nWcL001BDqzYEloyfKIkZ5wVEf5YYJv7sY5GveMnlq4ZJEvRGOE8sE6WqduXAegOkee86bpuRLeC9xONDvOLaHpAxDAWL1daNdw2S+DRmmpUcaH6fUM/KO184U+zn9Pu5WBzz9pEuN2yv5bxVB2X9zI6H5QN6NY2/h3zRJILtr6dbHHLLIH26ayAyMKxq14o2vaAub/7XDCDHiRUTfhQ71ok/aTgFSs0n6DZwiWdT2sQAkjAEsnOjd/m2YIlWdfsZAMaKq9sOnQllCSfMDxo74tA6r2DC1buV+BSC8YfJ25EqMXFuPKoqbndyDaFZM+C/GHiWucn9qn1dO/MjkwsJTE2EA5eelNWKX1psIdkBhqNr3dwPsl8J9rXracC2ukeZmngtgVFF42cQXiRgFw0TzRffcCXcfd8qqnSCu4xw4ifFcM0atnaLbOMIzNwYgGP7acDSbpitDtegfk0enqZge4QTUoACxJLGmM7e+9seabDwuJL1hzyY3bf0iBtU3Pn9Lov3PtpsGB7BEcXkbhYoRfQNtt40Hc2BDCAB8lSGtBadcCX3gE/OkBDrv6500mPA87d6pGZW0Ob6Xa+oKC4/vqz+PGrX8i5iNhfNN+86fhrC5cMhV6poZqCgQsr7sjVsF/m2sjfQMCuPXo9PCmDQ/uuICABMIAHSSxAy5XuzcU0gS6rbILBPph4vhP/dd8qCY/Re79YdQ1tzn65C/W8/lhgS2/Yjk5tuq7r0Wvh73KqGfHoo45C/15c1ojWYfFOLxhdNBoI/OSHmdbTdFzvWyWXlHPl8cgCJAAGOTlAizY5NIFGlvKL6skXWv+90O6uZaL863CwBV+Hf0QbM271C5hWVOoD9P8yxwaDy4uPYnMKapVw9ZjqC1iXVHAfWidvPRnw1yJ7ilNJX48z+88S+22nAp18c1CScl5MCBiABMmmZwRABbRox0MT6A7RfOcL3xzy4vhzkT26J9pRfBZdR0dNXaula8b6w69GLXGAQfxldOhSh062mLTO+dy9aAyi6e1o1XiBqvSc6jvmifO2eXw/3UriJorOFmGuzQI9z7uWSR+KG5jaHQAY/pTmsszxZY48nw19oDtEU4l6Z4K+Jr26auwqJ9p7VoiExgYfcVRimZt/rpVbhoVLup13NizF9PfV1bUtmrQ3Q6ECNo1cXl5hnX/4RyefHDQWGGagkXoTW5yZV8OUP84uAQPAQGYfjjPdbhAoZ/XJBTQUm1yOFpEklQO0TNAYcJm+maVP6iUAAAwGEM+M9RNNnkTLfY2nvECjd7D3zh4505rEtP1tvq1nYB7jtw31SV0Emr0C80kWmAHPyBnWtl5Z8nsAlRfoDtGlbHsuhAwjPXUyXddNCVeF90k19fJNwYxNZJO8w6ZY7j4fLNEjv6xiAGgoM7dmnzQPG+hQaO9b6pP6qqC4ntwoRQA8Uh1uUBQzQLfx+BhJkF8u0enW14zQrW+fNFKobpjO5FOEwCYktlgeF1zdxQzQHaJ7a45cDSPZPIXw61ybs/eia+p60eJzb1ZNfev5BzG/zSM0nfuJnC8CG3pHayWKMaCh9x/qDki7o23oZAsbj6y+ebXeIDuvbPJOG6gAGGDDYKRMAg3lfqxbLu10ID5Zl1e5cvqW7pMqC5Xr7p9LMq0hDkAlV8b9+1LFMNDICb7LEaSbP/uPNp69xT0+raJvBUQjhWpNeFcxd6sHeV8NSIAK4+0aw0B3iO7Me2ybMngC2ZUfyOqinV6pWVV97bSGCRWall21ZJc3Oc3AA5AoYmmCeaCFws4dVUt3e5NvVoR1depW5PsPct0B0CdVE0wIgztR5HMDAAN4ABJF3EbNPNBifSxpWHvIb4i0q5lmbnLHB91ne2iAUInvcqpmS7tjBEgADMXd26sooJE9j4A8Em/s4jBI2/TI1TAUhLr7SuzlQvWlv6/WvxZOdLlgVwASAENxTZiigO4Q7YmLTCyVuko0ZJI5TC75d6X0iUWh+pbufinVsx5gABK0z5tSkQKBhrjN7XZeWUOJHT2JB4hDJ1vsMAhq4PbtXlJLoeL0zgRhUCRlCWKSOWDg0vXvT1GKBVosnzcFY1c6ST0i8d9lDq8jPja1tCtirNAnxoVqQmWhyqR600PVa690AgZKSJUygG4U7YUluYv8n6baZOEOr7C4ksa+plodhGpCZaHKSM7/i8OYFU63zRKUU63KALpD5ObLPSBPqs9cjBEnrXO5aZrA4OJ+nxQhVBCqCZUl8Sxc9zB8aufV3ErzU6UkoDtER7j3XXxD4gZYHAZqm/y5yC44uqhvzUVlhapBBaGaSHzg/9NCmez7O6S2gaZ3JRpSHtAdItvDJ6TTnpZ6cvu7KZYbj/knZ1b2NdUqJVRHSmblxmOvUUHSDMjOy3e8gwuUPNZXKtAYRtQ3tN0ySxgxQ7q/foya0aJn5tX0jRFVRKgIVMf+i2/IFwLFYcR0q1umCeiWlVx9SgVaLGTS0jXjN2lbsfqJrsKdpuPqF/qh7zwi6+I28V6FfZym60rFm/pv82wtXTLqlGhpdIkFoDtEW78vPY6VujAu7rnmb/c0e5He1Ocshj01t7SbO6cv6JzQkFJf/USL25cfx9aw5K2YHaA7RJ5zjWxT/7NYituRrqZ697ngvqMurAho7j4fQvGaC1QoqpVxnx7UxRrQHaLv3i/04/jVL6j4rft6nNmsze6vwj70+fVSmlDUr8I/zt7iToVmVCKqEhXKbgWxCXSHaNQcHF20wyCI5H6t7uHPhXZXn8QnZXD6RooKlfhyKhT1n6R+6LoCqg+ViKpkfVaKZaA7RM1AXEr56oNk92t1D7/MtdE96t9LnJazJRQvCpn65eGoPlSiKnSe7AMtViOXd+hKKPXbVf5YYHfhQUx1HQOuSfrUXTV1LRcfxv57AaWGuZ9onwYqTnV2K6gK0B2iyygeWif/e6Gd1NXUfqKDiZ1bxQ/7eQXlow76LBA5hQJEMXoF568/0nksg8r9eqgmVBaqrIHuNSuKkAoB3dG53ZT3MqRgy4kAik65+2t1en3deOz164hCDb7VStFC0QVEFm46/hqFKXWn0T8lb4xqQmXRu29TcVItoKGmlvbEdM7sze5Szz50BYzB8TxGMKrT8amRUGgoOopTGZ/a5rGmKHBUkwouDqgc0GJxqpuNHdJGUlgh7x6m6bg+tU+tZsLnX28QCgrFhUKTqZBHzLB65pCmsi7dVBToDtFpF8/A/IV6XtSb6gFaJsOmWC7e6W3tnllY1th39laiUCwoHBQRCgrFRe6zuYfRvHCHp2dgnqJPncgj1QW6Q3T0Mr+o/tLjWO2VTtTLvZ/o2it8CUa2qcmZlX3jxS6hKFIyK5/YpaJwviN1f/xlSzFmhePfj2LzC+tV/DizSgMtVnF5o5NPjqw948AxJn8utFu5z8czKJ8Rx8PqLhgYXkH5K/f7dO5jlvFKsakbXB19corKGtnOhHSpAdBiVdU0XzaKoz5w6QoYtv823/amaUJSRiXbmWBH6KZumSWiECjOYHQPKPDLj+Mqa1TUYv5SagO0WBFvSzce8/9jgfStp1+Gr8aa6hkEmTi+S86obGnV8Bs4kUFwbOr0Tu9MEPXlqu4BhYyijnhbwnZWZJOaAc0XCFOzq+6YJ45e5kijtR6obTJypvUGfX87r2w02JU1LTzNuvoN2UGmkDVkENlEZqWekpLYKqN4Ucgoar66DazVDGixeO2C8PiS8/dj/lpsT6Mb7de5d89Ua7njdoNAVFvCu4qKqiYVH+uQC4lHFpARZGf76UBkjfyuPRLzDEWKgkXxqumZTrUEWiyBUBiVWLp870sajVD3MHya1dytHvcskzJyq+W8KVX5QoKRbCQeWZB6qF5q94XCRJHKfxUVi1JjoMUSCIQwrPdfejNulfSzt1KD9kqnY4YRJk7v8M73H+oam3gqVbmdV2U28ZCwiIRSJPK4YYRUbycUWmVjFB0KEFnWgJl7tQe6Q8R0SQXXzitr9mb3n2Y/J3fjKzXg599Ns5y50W3P+RBAExxdlJpV9aG4oaa+lZVeGJEiaiQAyUBiMM7beyEEyRs+zVLW2bcvc4riQqGh6FCAGkBzh2YALRZar/zCelf/3F3ngn+ZYyMn1p964TEm30+zQiu4UM9r19ng68/e2npmhcYVp2ZXlnK4NXUtLa3tDHKAV+GFeC1ejigQEaJDpIgaCUAykBg5Ie5C+Zc5z1FQKC4Umkr1QnJKc4DuEp8vjE0u33jMf/AEmadBqIRB2qbfTbWcvN5l9QHf4zciHtuk+IQUxKdWZObVcKqbYRJQtEHxGB7GT/DD+LQKn5APeNWJGxF4LV6OKKhso6URUCwonJjkMtZPlyhCGgi0WO3tgrdpFfetkuds8fhx1nP5zWupAVH8a7zZ0EnmP8621lrmOGmd84yNbvO2eS7f8xKMIuAP+Cv+Ef+FB/AYHsZPlJO2H2dZz97SOfbFt6dQh7bsSmOB7hAZIS1t/NC4kqtP4xfs8By11IERO0S9ArKMjC/Y7nn1STyKoqWVr0kGxpfSZKC7hL61tIIbmVAKC2HxTm+MhBixRFU5DBQN+JBZZBkZR/Y10sD4Ur0C6O5qbmkPiSnWvxb+8xwbJfT1yg/IFLKGDCKbqnBqVcnqdUB3qba+1TMw78qTuBX7fMatciK/ZkDFAxKPLCAjyA4y1ZtPo/VeoDtE02RNLe1p2VWu/rn618IW6XmNXuY4fJqVgqYXmA1IJJKKBCPZSDyygIwgO5oxnUxbvRro7mrnCzjVzXGp5fbe2YbGb9ce8pu0zgVm6CBtEypHoJUQ+ovcLSNJSBiSh0QiqUgwkq3WG1GYVR/QnwlkNHJ5FVXNqVlVQVFFj21Tdp4NmrvV4z+L7YdMMmfF5kakiBoJQDKQGCQJCUPykEgktQ/lHuoDmpKEwk5/sqnZVU6+OTdMEs7cidphELTu8Kt52zzHr37xxwLbkTOsh02x+HqcqUwzg3gYP8EP8XO8BK/CC/FavBxRICJEB3a5KralRJXVB7RsAlhtPD4IK+M05RXWRyeVeQTkPbFLvWIUd+pW5M6zwRuP+a/a77N098vFu7wX6nkt2OGJlnXOls6AP+Cv+Ef8Fx7AY3gYP8EP8XO8BK/CC/FavBxRIKI+jmVVH9CMSSAU8niCllZ+A7etpq61qrYF1m15VVNpBbe4vDPgD/gr/hH/hQfwGB7GT9R6u6aqqQ/oPmmU/h9V2Bia7bidBwAAAABJRU5ErkJggg=="/></defs><style/><use id="Background" x="0" y="-185" href="#img1"/><use id="Layer 1" x="57" y="56" href="#img2"/><path id="USACOW Guide" fill="#000" d="M400.39 232.72Q380.38 232.72 369.58 225.23Q358.78 217.6 358.78 197.73L358.78 125.44L380.09 125.44L380.09 197.58Q380.09 206.08 385.13 209.82Q390.31 213.57 400.39 213.57Q410.47 213.57 415.51 209.82Q420.55 206.08 420.55 197.58L420.55 125.44L442.01 125.44L442.01 197.73Q442.01 217.6 431.06 225.23Q420.26 232.72 400.39 232.72ZM491.11 232.86Q482.9 232.86 473.54 231.86Q464.18 230.85 457.7 229.26L457.7 212.42Q470.52 213.71 490.1 213.71Q498.89 213.71 503.35 211.41Q507.82 208.96 507.82 202.91L507.82 199.02Q507.82 194.27 504.94 191.82Q502.2 189.23 497.02 189.23L487.94 189.23Q472.1 189.23 464.18 182.32Q456.41 175.41 456.41 159.71L456.41 154.53Q456.41 124.58 490.82 124.58Q498.89 124.58 507.53 125.73Q516.17 126.74 523.08 128.18L523.08 145.02Q503.35 143.73 493.27 143.73Q485.06 143.73 480.89 146.32Q476.71 148.77 476.71 155.54L476.71 158.42Q476.71 163.89 479.74 166.48Q482.9 168.93 489.24 168.93L499.75 168.93Q513.58 168.93 520.78 176.56Q528.12 184.05 528.12 196.29L528.12 202.34Q528.12 214.43 523.66 221.2Q519.19 227.82 511.13 230.42Q503.06 232.86 491.11 232.86ZM537.05 232L570.46 125.44L597.38 125.44L630.79 232L608.62 232L601.85 209.68L565.56 209.68L558.94 232L537.05 232ZM570.6 190.53L596.81 190.53L583.7 143.73L570.6 190.53ZM676.87 232.86Q663.48 232.86 655.27 228.69Q647.06 224.37 642.6 212.56Q638.28 200.61 638.28 178.43Q638.28 157.12 642.74 145.31Q647.35 133.5 655.7 129.04Q664.06 124.43 677.16 124.43Q686.95 124.43 695.59 126.02Q704.23 127.46 710.42 129.47L710.42 146.03Q706.1 145.02 698.18 144.3Q690.26 143.58 681.62 143.58Q672.98 143.58 668.38 146.32Q663.77 148.91 661.61 156.4Q659.59 163.74 659.59 178.58Q659.59 193.26 661.46 200.61Q663.34 207.95 667.94 210.83Q672.55 213.71 681.62 213.71Q699.19 213.71 710.42 212.27L710.42 228.69Q697.61 232.86 676.87 232.86ZM765.14 232.86Q749.45 232.86 740.09 228.69Q730.73 224.51 725.98 212.85Q721.22 201.18 721.22 178.86Q721.22 156.54 725.98 144.88Q730.87 133.07 740.23 128.75Q749.59 124.43 765.14 124.43Q780.84 124.43 790.06 128.75Q799.42 133.07 804.17 144.88Q808.92 156.54 808.92 178.86Q808.92 201.18 804.17 212.85Q799.42 224.51 790.06 228.69Q780.84 232.86 765.14 232.86ZM765.14 213.71Q774.22 213.71 778.82 211.12Q783.58 208.38 785.59 201.04Q787.61 193.55 787.61 178.86Q787.61 163.6 785.59 156.26Q783.58 148.77 778.82 146.18Q774.22 143.58 765.14 143.58Q756.22 143.58 751.46 146.18Q746.86 148.77 744.7 156.26Q742.54 163.74 742.54 178.86Q742.54 193.7 744.55 201.04Q746.57 208.38 751.18 211.12Q755.93 213.71 765.14 213.71ZM891.72 232.72Q879.05 232.72 871.85 226.24Q864.65 219.76 861.77 208.1Q859.03 196.29 859.03 178.72Q859.03 160.29 862.2 148.62Q865.51 136.96 874.01 130.77Q882.65 124.43 898.34 124.43Q908.57 124.43 916.78 125.73Q924.98 126.88 934.34 129.18L934.34 145.6Q928.73 144.88 919.94 144.3Q911.3 143.58 902.23 143.58Q893.02 143.58 888.41 146.9Q883.8 150.06 882.07 157.55Q880.34 164.9 880.34 178.86Q880.34 192.83 882.07 200.18Q883.8 207.38 887.69 210.26Q891.58 213.14 898.78 213.14Q904.25 213.14 909.58 212.27Q914.9 211.41 918.79 210.11L918.79 189.38L899.64 189.38L899.64 172.53Q903.96 171.95 910.87 171.52Q917.93 171.09 924.26 171.09Q933.19 171.09 939.24 171.52L939.24 232L923.26 232L920.38 224.8Q915.05 227.97 907.42 230.42Q899.93 232.72 891.72 232.72ZM978.55 232.86Q968.04 232.86 962.14 226.67Q956.38 220.48 956.38 209.39L956.38 154.67L977.69 154.67L977.69 203.06Q977.69 209.25 980.42 211.7Q983.3 214 988.92 214Q992.95 214 997.13 212.56Q1001.45 210.98 1004.76 208.38L1004.76 154.67L1026.22 154.67L1026.22 232L1008.5 232L1004.76 224.08Q991.37 232.86 978.55 232.86ZM1044.94 143.3Q1043.5 143.3 1042.63 142.58Q1041.91 141.71 1041.91 140.42L1041.91 126.3Q1041.91 123.28 1044.94 123.28L1061.35 123.28Q1062.65 123.28 1063.37 124.14Q1064.09 125.01 1064.09 126.3L1064.09 140.42Q1064.09 143.3 1061.35 143.3L1044.94 143.3ZM1042.34 232L1042.34 154.67L1063.66 154.67L1063.66 232L1042.34 232ZM1105.42 232.86Q1091.45 232.86 1084.54 223.79Q1077.77 214.72 1077.77 194.13Q1077.77 179.44 1080.36 170.66Q1083.1 161.87 1089.43 157.7Q1095.77 153.52 1106.86 153.52Q1116.36 153.52 1126.58 157.12L1126.58 122.13L1148.04 122.13L1148.04 232L1130.04 232L1126.58 224.66Q1122.55 228.11 1116.79 230.56Q1111.03 232.86 1105.42 232.86ZM1112.9 215.73Q1119.82 215.73 1126.58 211.41L1126.58 172.53Q1122.84 171.09 1119.82 170.37Q1116.94 169.5 1113.77 169.5Q1108.15 169.5 1104.98 171.52Q1101.96 173.39 1100.52 178.58Q1099.08 183.62 1099.08 193.12Q1099.08 202.19 1100.38 207.09Q1101.82 211.84 1104.7 213.86Q1107.72 215.73 1112.9 215.73ZM1196.57 232.72Q1183.46 232.72 1175.98 229.26Q1168.49 225.81 1165.03 217.31Q1161.72 208.82 1161.72 193.41Q1161.72 177.57 1164.89 169.07Q1168.06 160.43 1175.26 156.98Q1182.6 153.52 1195.85 153.52Q1207.66 153.52 1214.42 155.82Q1221.19 158.13 1224.22 163.89Q1227.38 169.65 1227.38 180.16Q1227.38 191.39 1220.9 196.14Q1214.57 200.9 1202.33 200.9L1182.89 200.9Q1183.18 207.09 1184.76 210.54Q1186.34 213.86 1190.38 215.44Q1194.41 216.88 1202.18 216.88L1224.79 216.88L1224.79 229.41Q1217.16 230.99 1210.97 231.86Q1204.78 232.72 1196.57 232.72ZM1182.6 188.22L1200.17 188.22Q1204.63 188.22 1206.5 186.5Q1208.52 184.62 1208.52 180.3Q1208.52 173.97 1205.64 171.52Q1202.76 168.93 1195.85 168.93Q1190.81 168.93 1187.93 170.66Q1185.19 172.24 1183.9 176.42Q1182.6 180.59 1182.6 188.22Z"/></svg>
\ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="354" viewBox="0 0 1280 354"> + <defs> + <image id="img1" width="1280" height="720" + href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQAQMAAAD1s08VAAAAAXNSR0IB2cksfwAAAANQTFRF////p8QbyAAAAIdJREFUeJztwTEBAAAAwqD1T20JT6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeBrE3wABB8CymgAAAABJRU5ErkJggg=="/> + <image id="img2" width="240" height="242" + href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADyCAIAAAD8/9/OAAAAAXNSR0IB2cksfwAAO+VJREFUeJztnfdfE9n6x7//gm33urquq9t37+71ith7772CXewVO7a1Y6+odJAuHQREmvQmXaoUpRM6hBaS8P2QuFzEzGQymWSSwOd1frBM5rT3nPOc9pz/6+gTqYTCjrY2fmMTj1PdnFdYl/CO8yr8o5NPjqHx22OGEbpH/eds9RizwvHXuTYjplsNnmA2UNuk32hjigEP4yf4IX6Ol+BVeCFei5cjCkSE6PI+1iFqJADJQGL6RK7/YzsBqqt2vqC6riU7vzYkpsjxZc5ts4R9F98s3fNywhrn/yy2/36a1TcTzQdqm1LHlxripngtXo4oENHS3d77/n5zyywBCUAykBgkCQlju2xUV31Af6aWVn4ph5ucWXnfKmnP+RDwNGmd878X2n0/3eqrsQyzSz0gaiQAyUBikCQkDMlDIpFUJJjtMlMt9QH9SejTQ+OKLxvF7TAIXLzTCzbA1+PM2CKYPCBhSB4SiaQiwUg2Es92+amKeinQvHYBDNPEdI7769wDF9/M3uz+3VRL1kmlHZB4ZAEZQXaQKWQNGWS7jNlRrwOazxdWVDVFJ5XdNk9cdcBXe6XT4AnmrBPJSEBGkB1kCllDBpFNZJbt8la2egvQQmEHt4nnF/rh4sPYJbu8Ry11+Nd4FbUo5A/IGjKIbCKzyDIy3numR3oF0M2t7QERhVeM4qZscBk5w7q/lgwza+obkE1kFlmGnf06orC5pZ3telCGNBlogUBYWd1y7l70xLXOA3oHxCRwT1jrfPZeNMxrFAvbNaNAaSDQ6F6r61pehX08cCl0mq7bIKan2wDHV2NNh0+z/EW0GjJzk9uag347zwQduhx64UGMofFbI5sUS5cMO68sJ58cV/9c76B8nzcFCPgD/op/xH/hATyGh/ET/HDn2aC1h/zwKrwQr8XLEQXjPQmKYpqO6/6Lb/zCPqCINNIO0Sig29sFJRXc5x6Zy/a8/H2+7cAxzAAxYIzJkInmP8+xmarjuu104B3zJDuv7OCYoqjEspSsyvcf6ko53Jq61gZuW2sbv51PtQEUitZu8BP8ED8v5TThVXghXouXIwpEhOgQKaJGAgYwlB0UCwoHRfTcPRPF1a5Z8yGaAzRAcn+du+/vEDRy/bWYadIwuoINeuRq2H2rJLSsoXElWXk1aNuUsJyBKBARokOkiBoJQDKQGKbGsigiFBSKC4VG+RtUA2kC0GjkAiMLt5wMYMRQBjGT1znfMEnwDf2ApleoSh0zElPGafIL/XDDNGHKepfBExiAG4WGokMBohjZzh8DUm+gq2pavILydxgE/TrXZhDdbRWwVkfOtJ6u63bg4htL14y41IqiskYV74hhqxSXNyKpVq4ZSDYSjyzQXpxH0aEAUYwoTBQp25mTS+oKdG19a1BUoe5Rf9iX9AZPMCUx/Jq/3dPcOT00rjinoLa+sY3tbNERko3EIwvICLKDTNEbPKAYUZgoUhQsipftbNGUWgJd19B60yRh7lYPegZl/9HGw6dZ7j4fjIEXRmAasxECGUF2kClkDRnsT9fiQsGieFHIbGeIjtQM6OaWdguXjDlbPej1rd9MMJuu62rhko7xFttZUayQQUuXDGT2m4k0F/ZRyChqtVuOURug23j8pHSO3pmgETOsZJ3AGtzJsdv+i2+CY4oKSxtU3D5mSshmYWljSGzRgUtvZmx0+0bGLSsoZBQ1CjwxndOmPuNFNQBaIOgc2t8wThi7ykkmlPtrGaN92nY60NYzKzOvRn3tQjlV29CalV9j55W1zSAQBSLTnCYKHMVuaPy2lMNViyVGNQA68R3H4E7Ub/NsZWpgBo01Rad55UlcRm51k7r1m4oQCiEjrwYFgmKRdfUUhX/6duTbdxVsZ0K6VBpobjMPhuCwKbLtVO7ct7DmhfvrXM2YWGVcKBYUDopI1tmhYVMszJ3Tuao9hlZRoJua22NTynWP+svUP/4y12bNQT8bj8xeu72dulBEKCgUFwpNhsZitLHOUX9UDbdZRTs9VQQaTYirf+7Go/7UZ+W+Hmf636UOMPWSMir7GmaKQkGhuFBoKDrq583+Nc4MDQ0qSDXLWbWAFgiFeYX1Jo5pP8ykumsZj41a4nD2bnRoXHEvmb5gVii00LiSs/eigTXFvQMoc1QQqgmVJVClrQEdqgZ0flH92kN+QydZUGwtBmmbrNzvG59a0XeyX07x+cK3aZzVB3wHUfYrgmpCZaHK2E77Z1IVoNt4fJ83BVM3uFIfoMzb5uERkNeHMoNq5ws9A/Pnb/ekPhBHlaHiUH1sp/2TVAJoDAEDo4rGrXpBpcvDMPH7aVZ7L4QEaMoGMZWSeOvivr9Dvp9uRWVEjipDxaH6mlRjmMg+0EVljc8c0mDAURr8jTdbtuflE7vUXrtKohzVNbQ+tU9FUX9NbVyO6kMloirZTjjbQOOzPnwllPq+Xr0zQSUVXBUbh2imUMgoar2zQRSrBpWIqmS9nWYNaIxC4lLLdxgEUenXBmqbTFrnYuL0rhc6mmBXfIHQ1Ond5PUuVJxQoipRoahWFquJHaDx9ae/r9Y96k9l8IHR9HRdNwfvbNUZefQq8Xh8x5c5Mza6UZl9QoWiWlG5bPWiLAAtEAiT0jkr9/tQ+ei/nWLx98OYxHROX9vMolD4SRmVFx/FDpsinWlU68p9PqgyVjYzsQB0WnbVlA0uVMyyH2c9f2qfpklHONVaANTY8R0qhUrdwUpJzapSfiKVCnRLa3tMchnsB6nFMWSi+fztniExxcpMXp+o6E1sMapmCIVzA9N0XaOTyppblTpMVB7QQqEwIqF0++lAqWc5B4wx0Tnq7x1c0LfHSAWFSkHVoIKk7k1HRW87FRD+tkSZJ+eVBDR6q8y8mlmb3KUunQyfarn6gG9RWaNabCfvnULVoIJQTcOl+SDur2WC0WRmbrXS7EYlAV1QVH/ZKE7q7jk8gG86IKKwj2YVFyoI1YTKolKnlx7HKm3LhzKATs6snL3FXeopie+nWz22Tek7XaJGQmWhylBx5DU7SNt09mb35IxKJSRJ4UDX1rfqXwsfLO07/m6q5anbkTWafhhb84QqO307Uur9BwBA/1qYEjYsKBZoTnXzmbtRX48ja5v7axn/NOu5ofHbvp1GaipU3A2Ttz/Nfk6+hR0YAAYgodDEKBDohsY2S9cM5JP82/11ng3a5oJi1dpW2yeZ9KG43uBOFKqSvK4BA5BoUKSHKkUBXV7ZtOts8HekK9sDxpiMWeH4JrZYY5ZOWlrb0QKlZFa6vc698CBm0/HXC/W8xq168d+lDqOWOmivdJqz1WPdYb8TNyJsPbPiUspLKrga47cJlRgeXyLV1QSQABjAQ0HJUAjQQmGHvXf2b/NsSTYe9R9t/Pt825umCS3KnXhXkJDlBi7PP/zjXcskIAuCh02xlHgtp9jb9B8L7Jbufonhv7Pf+6raFs1Y2EdV3jZPRNZIvJABCYABPBQ0N8080AKBMDal/K/F9uTb6H6bb2vnlY26ZDwByhdaZQuXjG2nA/GJyuR666txpj/Osl59wBdDiLxCTTC6qmtbHLyzf59vRz5qAh6ARBGTs8wDXVLeqHvsNbmnwIFjTI4ZhoNmDdjZXFHVdPxGxOhljrRdkQ/U7nT7uf10YGp2lbpPwIvvAzlxM4J85xnwACRAhfEEMAk0MlNY2rjlRAB5/f0wy/qJXaq6z2m0tws+ljSINqAxdmPnIG3TXeeCU7Oq1N0Ma+PxnzmkSd3GBFQADLONGpNA1ze2wTb6iTQbqDO9s0GlHC6D8bKinIJaDAB+lcVLC5Xw3VTLQ1dCoxLL2M6fvCrnNOHjJF9NAyoAhlm33EwCbe2Wob3CiSQD6JSX7PLOyK1hMFLlCy1KRXXz4l3eCrpN+etxZuNXv0jPqVZ3NyOZeTXL9rwkP18HYIANg5EyBnRTS/vsLe7kvuOn6rh6BOTxeOpdT6WcpofWybTvf6ASYGLqXw+HSc12XuUSr13gFZQ/XZfMNQWAmb3ZncH9DswAjV7DyDaVvJKGTbEIVv/9zTV1rYeuhEndvSB/6OzNdnurmhsXGnoTWyx1mPHYNqWOIcODAaAxvLN0zZiwxpkkxT/MtD51O0rdDwUKhEKzF+9GUHNYwQjTp29HqfvouY0nMLgT9cMsa5KcTljzwsIlg5GcMgB0fGr5hLXOJF0wKubq0/gPxQ3yx8WuEtI5FP2HMBWGT7W0cs1Q98lNVP21p/Ek05oYO45f8yIutVz+uOQFup0vuPAwhtx35XRdN5iD6r4YBotw++lApm6npR4wQKyoUuyGHkULVZ+WXUV+9A4InbsfLf8ZJXmBtnTJGDqZ7CQwWu6QmGJ1b2MEAqFf6Aclo9wVRBsE1NvwAADAgHwkPXSSBSw6OSOSC+icgto/FpDdFPHtZItLj2PVffqpo3M5sHmztAUjxQW0bVn5NereKAADwPAtafP3+wLb7Hy5ZnXpA41h6ZFrYeSdyA6DIA1wQsfjCdC6/E766So0DJti+cw+TQP2vQAGIEFuoB6+GlbXQH/GgybQ6IJjkstHkY6Q5m3z8AkpoJ0y1VF5ZdMtswTaF/7JH2C4bzr+Ov19NdslwYCABMAgyeyoJQ5Ai/aeFppAw8bHp0aSrB9mWgdEFmqG82aU78xN0n2JKDQMn2blEZDPdkkwICABMIAHSWZ3GASm0V1UogO0QCj8+2HMCNLFhWOG4Wp6dfaXsvfKJrf8lBD6axmfvBnBdkkwI4ABPEgyC7QAGL3LLugAnV1QSz4d+9ci+7zCOnUfxHTp0JVQ5aykkIcZG93U7qJiiQIYwOOvxfYkmYU1m51fS+PlMgPdyuMb2aWS2PXfTbO8a5Gk7vt6u4QR4UI9T9Zp7ic6ElGm/rsUxQIe9yyThk8jXBL/apzpY5sUGmuHMgMdElNEMkOOlmzDkVfqvv2ou2rqWv9D2pYoLQydZJGcqQzXFsoRr12go092D+U0HdfAqEJZ20XZgOZUN6877Eeyhokhamic2u9A6q6C4vqRpCMYpYVB2qZeQZowLuxSWHwJie0KK2DtIT9ZV0llAzr8bcm/FxIeFxs8wXzLyYBqzXIWk5lXw+CZFHnCgDEmNh5ZbJcHk0Lvt+1U4DcTCOdDf59vC+hleqcMQMOgOW4YQdJHLNTzCo2TLXrVV2pWJYsz0D3CM/s0tsuDYYXHlyza6UWUX8B27Hq4TJa0DEAnpnPI74U2c07XjInn7krJquoDWnECMObO6SRZ/mXO84R0DvUXUgW6vrFt6ynCzQzoDceuclL3nbsSlZVfo6CjVrIGFLKdVzbb5cG8gM04Uvc0W04EUF/ToAp0ZEIpiaOnEdOtzt2LppsjldbHkgbyzelKC1+NNfUNLWC7PBQiwDNiOmEhAzzgR/FVlIBua+OfvBVJZD0P0Op0uJ/wToZ+QY1UW9+q5E39ROHbyRYw6NkuD4Uo8R1Hl/hKANEqaWQbtf5fOtBCYUd2fs2kdYQnrEbOsHL0yWngaoiPth6CkTd/u2osrMyzLVOYSzh21djEe+GbQzI9OnGtc1YepQ200oFGE/XMIY1kbmXVAR9F+0hlV+cfxFC5hFzRYfFOL01aseqhyurm1Qd8ifI+eIL5U/s0KluRpQOdml2F7oDI3oBhd88ySWPch0oUhmJU7udTaEB3jG6X7ZJQoIDQfStC5xDAT0ffP4WCxSUFaKFQ+MgmmWTeat42j4+lan/6lVzxqRWwOtjdn/TznOeegXlsl4RiVVTWuGAH4Zz04PHmD6ySpV6oJQXo+sa25XtfkhS066tcZV7axYpguRoavx1MbHQpvHnGsFv/1bsc9fY7I1XgyP11Hkk5LNn9srZBitUhBWivoHyS5nnSOpfecJWgQChMTOeMXu7IFtA/zLJ29c/lNmvmsLu7gNPk9YS3DKNN8QiQ0k2RAd3GE+gTnxocNNb04qNYRrOjuqqoat54/DVbQGvGIVmKAlQkLh4PXw1rIx0ZkwGdnFlJshUJ/xVBebpb3cXnCwMjCxXqz44kPHqeou4OdqkLUJFQ98dCuyTS6+HIgLZ2zyS6wApW3aKdXmUczZwWlaja+tbZW9yVT/PwaZaZnVOwvaN9Fo1YFu30JponBZDAkuTnhEDX1LWu2u9DVMqjltibOGraRhmpKq9qmrvVQ5nTHX8ssGPEQZZ6ydTp3aglhKuzwBJwEv1WMtAYBsHeGLOCcBi05qBfbEqvK2g0k8YOqd9ONiO/cIOpMEjbWP9aqLp7uKShuNSKtYf8iIoFWAJOoiO0koFubOLZeGQNnUS4mvDUPrW511h13RUdlzplmeHgsQ8VTvMYo99nGr5wD+o9xkaXMGAwdkgjKhlgCTiJrsOTDPTH0oaTtyKJHBN+M9E8Kqms95Vzh0AgMDe3WLp6139nnVU00L9MvTpn2b6Ll640NfWigYpYQCsmuWwIQXsKLAEn0XKeZKBf+L0n2Smy+oBvL7xiHjQHBgZu2rRpvUhacwyGjr/ff/QzxlH+19hHv0+/tGadLmLZsGHD48eP29o0xMMJdTW3tJNYHYATiEr8oQSg8X1cfRJPvJfP5Nz9aHpOQNRaDQ0Np0+fXv+Plq3aOn7e8W/GPWCW5q+1H4+adW7B8p1dEW3btq2goKC3GR7I7oUHMUSXhwNOICqxSCQAzW3mLdtDuNz9+3xb72CNOntMRTwez9fXd/3nWrNWd9aSfT9Ovt5fi5l2etiEu1MWHFm1ZlOPiIyNjRsbmb/ST8XlE1LwxwLCCWkgKnHpVALQReWNfy6S/KLO+xKP+muG10CZVFlZee3atfVfaO06nQXLd/0+/eLAMU/kQXmA1jN8GLOW7F+9duOXsRw9ejQ3Nxc2D9vFoFQBs43H/IkmlIBokaR7O3sCjWb8ZXAB0Vu+nWzh5JOjGQ6pqIvD4djZ2W3evPlL1LpbIFMXHh4x6SaMEIpwD9B6Onjsw+8m3IHpsnC53rp1G4herqOjY2ho+P69ZKtRUwXMABuRV0EgClC/tDp6At3axn9onUxUB7/Nt414W6oxbr4oKiEh4cSJExifkQDdaYGs05m7dM/E+cd+m3b52/F3YQ0P0u609rq3DjAKB44x/krbaMi4+z9PuTp27slZiyW3yj2kp6fn4+PTqyxpYAbYgBwRjQD1y3PZPYHOzK0hOd29cIdXRVXvmkVqbW29ceMG2kipzIm1bv0GGMGLV+zQ1btg9SLe2i3T0Pjt3w9jMcTBOMbsxTtb99Q9+ncXrtBbsXozSav8pWB4lJf3rsUswLaQeIc0QM384hLXnkC/DCkguSlxz/mQ3rBftLvS0tLQOlLHrktGRkZ1dXUtre2VNc1llU2lnCZOVTPGMc3NrZ6enjReuHHjRmdn517VSAO23edDiGgEqF961O8JNJoTkjvHTRzlvdNFvcTlcvft20cDPtgnHz9+JHptc3Pz3r17abwWHUVBQYESC4B9ATkiGn+cZQ1cezz/GdD4IPTOBhFtkvxhpnV0ktpfqk5dbW1tr169kmo6fyldXV0bGxs+n2wPRlxc3Pbt22kwbWpq2qum8IAckbt/gKp3JqiHyfAZ0MXljXOId0jO2eqhATcAURR6djSxBgYGNJg7c+YMjA3y9/N4vGfPntH4Wvbv349Bau9ZO6xtaJ27lfBOltlb3IvKPvu8PwM6MYMzagmhL2QY0Jp9uru7Ghoa0DzTaERh6Xp4eFCxdGNjY2lY53i/iYlJaWmvOVohEO69QGhG/2exfQ8PR58B7RGQN4zAj1t/LRNbT43y5UquxMREtIWy0gZj49y5cxRpg+Xw5MmTbdu20WD65cuXii4B1ZGdVxbRGviwKZZu/p+dMvwM6Me2KUQGNAzw+LQK5WaENcEeuHXrFg174MiRI2jX29spLTyhFU9JSbl06ZKssawXTeHV1tK5gkQdhTb4p9mSJyqA68PnKd0f/gzow1fDiI5jzN3mWVmjUZ7MSZScnEy+LkgkR0fH6moZ9gW0trZGRkbSiEhHR8fLywsfnuIKQXVUVduyYIdkb2zA9eCl0O4Pfwb0cuI9SdsNAlvbesUMNKxnc3NzepC9f/9e1h0XGD7u2bOHRnTXrl3rJbvw2ngCvTOEl2Iu2eXd/eH/Ac3nC0n8xl59Gq/0jLAgtHlo+WhYz7BPHj58SC9SxLh161ZZY8RPHj16hM+P2RJQTV1/9paIzJEzrbu72f8f0AXF9UQG9EBtU2s3sqO2GqMPHz4YGBhQX+ju0tmzZ5OSkuhFWlRU9PTpUxqR4sN7+7bnyoJG6rlH1iBtyXACWqDb9eT/gPYOzif6CEbMsA6OLmIjI8qWhYUFDet57969MTExLS00xxiwUmA8dD89QFH4Bi5fvtwbLOmQmKKRMwjNh+6Xg30Cmi8QPnxOuMnuv0sdNOmGPCJVVVUdOnRIVqqgW7du1dT03CUjkzA6tLW1pRE1DI/ExESNt6RTMiv/u4zQsQHQ7Voh+QR0c0u7/jXJ1y8P0DKZu82jx3qM5onP56Pfp4EUFBsbKz9SFRUVNCzp9aIpPI23pIHfvG0eRN5ngG7XHv1PQFfVthAdu4KNoqPvr/GL3vHx8V0HYKlLV1f33r17TJ0l8fX1pbHOArm6ulKc/FZTAT9ASDTGA7oAWPzkJ6CLy7njV7+Q+PQ3E813nQvWbNeXzc3NRkZGNEjCsCwlJUV6BNQEm+f8+fM0FnT+/vtvNPAafEYL+AFCIke4QBcAi5/8BHR6bjXRNUTDp1mduh2pwf57MJiDzUBjmyhadGtra2YxCgwMpLHBA7aKi4sLvgcGU6JSAn6AEChKnrSYbg2AxU9+AjoqsZRoWuSXuTa3zRI1eFtSTk4OWjga7SKM17w8hr3q19bWXrp0iUZi8BmEhYUxmxjVEfADhET3vgJdACx+8hPQJHN2WssdHbw18L5HsXg8nrGxMQ2Adu/e/eLFC/JNz/QUExNDYwoPunDhggaPDgGhFrHP+S7XGp+AdvTJJnp06gbXwMhC9jKiWJWUlNCeqlPQ4ZH6+nraU3gw6BXxjamCACFQJKIUAIsf6wRaKOx0YEr06IIdnlRuH1JH1dXVOTs70zuTkpiYqLhBWHl5Ob2DjA8ePMjP10w3QICQaIsSAgAWT5x2As3nC+9aJBI9umq/74dizezIgoODYQfLCg0+AJgEil7LePbsGY01y+3bt9vY2GjkdAcgBIpElAJgYNwhBrq1jf/3w1iiRzcdf12jiZPQHA4HRieN5vnEiRORkQq/MjA3N5fenuyDBw9mZGQoOnnKFyDcRHzNDQAW++joBLq5pf3EzQiiR3eeDf7SnYcGKDw8nEa3vmnTJisrKyX4x2hsbAwICKB3RsvJyUnRyVO+ACFQJKIUAIsXCzuBrmto2/c34bEt/avhbOeFeTU3N9Nbwjh58mReXp4SBl4waWpqam7cuCFrCteLZmCKi4sVnULlS/8q4Z1sABgYd4iBrq5r2WFAuIHa4E4U2xlhWDAxg4KCaIAC2dvbK9NCjYmJwQCURjofPXqkeZ7SgSIRpQAYGHeIga6sadl6ktD914UHMWxnhGFlZ2fTOyRy/PhxmU5Yya+2trYnT57QSKqOjk5oaKiGTeEBRSJKAbD4iGAn0Jzq5o3HCM3tS4816nbN1tZWFxcXGsaG+Ky18icQ3r9/T28K7+HDhxp2kBYoElEKgIFxhxjossqm9UdeET16XYMOXwHHnJwcGlN1aPAuXrzI1lZ6CwsLGjsB8RlERUVpkksaoEhEKQAGxh1ioEs53DUHCWf4bpomsJ0RxtTQ0ODt7U1jfnfbtm0susJITEyksXcKH6GRkVFRkeYcNQKKRJQCYGDcIQa6uLxx+V7COzbvWdI8KqeCAs309rI9ePBAqncvxQlmkqurKw2mYSbBBNeY8yxAkYhSAFwscugvBpq7Yh8h0HctEtnOCDNC50tvV93JkycjIyNZHGCByIKCAnpTeLt27aqp0ZCblUnWswGweEu0yIbmNK09THiF1pceS9VRoBlQ0qAZHTeMDdYtUVj/KSkp9M6zODg4yHnkUUUEFIkoBcDim+c7gS6vbNqgTzgovPpEEwaFhYWFMBto0LB//34VWaTgcrnXr1+nkYUzZ87Ex8drQCMNFIkoBcDlXYPCyprmzSc0eR4a5u+jR49oNG87duzIylIhF5Vg+tixY7L2M7q6umfPnsXoUN2ZJpmHBsDAuEMMdFVty/bTgUSPnr0XzXZG5BI668zMzEOHDsnqyQXPX7t2TdW8XtjY2NDbhRcWFkbbc4iKCCgSUQqAxedkO4GurW/ddY5w28fxGxFsZ0Qu0fZLtG/fPhX0SyR2SUMjO+fOnUtOTmY7+XIJKBJRCoDFjgk6gW7gth28HEr06IFLoWrdUwUHB9PwVbdp0yZDQ0MVPHba3NyMRnrXrl2y5mjLli3W1tZsJ5++ACFQJKIUAAPjDjHQLa38M3cJt31sOxXIbVKtbpe6YDAcPnxY1rpfLxpIpaWlqabRWV1dfevWLRqZwlfK4XCkR6CSAoRAkYhSAAyMO8RA89oFJBMi6zonRLhsZ4eOWltbo6KiaEzV4SdeXl6sT9URCZ9ZUlISPQ/WTk5OajqFBwjXkU4ui28P6gRaIBQa2aYSPbpkl3dWvloWAZrYy5cv06j1I0eOqPi2HnxsN2/epJE1fX39169fq+MuPEAIFIkoBcAC4T9HsCBL1wyiR+du9YhPVb/LKDCit7Ozo2Fr6urqMu4+RhEKCgqikTu06/gSVHBsIFWAkOQ6LAAsfuwT0K7+uUSPTljj3N1dqbooMzOTxqZnGBs3btxQi8stYUnDfqBheMCSVkeXNIAQKBJRCoDFj30COii6iOiioX8vtHvqkKqawyMitbe303Mfs2/fvvj4eBjfbOdAumh7lV4vmsKrr6+XHofKCPgBQqAoEVGgG/SP//JPQCdlcIZOtpD49MiZ1hcfxbar1RXfCQkJO3bsoFHTJiYmauR8CJa0r68vjWxu3LhRvbwdAD9AOJLgSlmgC4DFT34C+v2HOiLHYcOmWOhfD2tRn4PfaF+vXr1Kb3JDoe5jGBfarbKyMnpXLMMeq6hQm6ER8AOEQFEiokAXAIuf/AR0eVXTrM2SL0X+13izrScDxLPWqi+M32Eg0qhg0Pz48WP1sqzEioqKQotLI8vPnz9Xl8Vw4AcIgaJERIEuABY/+QnohsY2ou0cA7VNlu15WVHVzF52qAo4lpeXX7lyhUbtHjlyBK0d2zmgI0BpaGhII8v6+voYOquFp3TgBwiBokREgS4AFj/5CWheu+CyUZxki3u08cS1zqozFS0QCJtb20sruNkFtXEp5aFxxW9iiyMTShPecdKzS52cvbduldl6xsDfx8dHHZtnsd6+fbt7925Zc62ru+n+g2eJKbnJmZVRSWUoRoTopLL099WFpY3cJl47X1UKJDu/FhD2J5jiALq8f8Z4n4BGVVq5EU5F/zbPNiZZ4b6CqAgF7fAy+8qT+O0GgasP+s7f7jljo9t0XdfZm90X6XmtPuA5R8d43LzT85btXrOWqjsLHR2do0eP5ubmsp05+qqsrLxx4wb1Kbx16zYsWrFjwvzjk5ffXrXXdcku7zlbPabrdpbk3K0eK/b5bD4RcO5etOmLdxm51arwncemlP8+35ZkErorjf+71i0xnUN0LzJsFzd/NusbrYV7QN7qA75ElxJ8GYaMuz96tgHIllq7Bw8e9PPzU4uel0Rit+1SM7tk5fYJ8459N+FO/9HPqBTjV2NN0WQYO76rrG1hkWv313mDJ0g2oAEtOueuJ/8HdG1969BJknHBbx7bMHaTCHXx+UI0yXctklYd8B0x3YroeyMKA7Se/mvsw1Gzzs1csn/1WsnDJowFrays1GiqjkgYDb9+/ZpodLh2nQ6+be25p/CdDxzzRKZi7CeaF5u9xf3M3ai07Oo2Nua7jGxTiWp/yCTzmrr/rRv8D2jYpqOWEl4Fd+ZetEC5XQ9a5fD4klO3o36c9VxWlLsH1N/3k25OWnB04QoJ57319PTUfZdwl0pKSiS6HFm6atuMxQd/nnr1a+3HtIuxE52J5ttOB3oHF1TXtSgTBtg85+4Tbu3/z2L77velfHZ5/RZih2BrDvk1cpW3iTQ7v+aOReJfiyWvDNHBWusp+tm5y/as7mZbYyxoZGSk7sZGd7m7u3df8IetvHjF9h+nGH4lH8rdw8gZ1scNIzAKV1qm0LSR7LPbeOx194c/A/ra0/gBYyTPjIxe5pj7UUmOKRq4PIzwBhHM0cjVxox7MG7eCfS/4vqG0cngpWyqoLKyMjMzs080r98wY/EB9E6MF2N/LZNRSxw+FDcoZ7yYX1g/ZoXk21UGaJlcMYrr/vBnQNt5ZQ8hGHV9Pd7sZXCBopPezhe8iS0mObErfxg0xkhrtgHa6e3bt0dERKjFtg3qAmHv378/fPgwaJ68QH/w2IeKK8lZm9xtPbO6rnBVnHxDCoiWVL6ZaI40dH/4M6ADIgt/IFgu7yfyOKPo7zGvqB5mD9F1dEyFwWMfTFl4+PyFi0rwW658cbncp0+fzlu+a/jE2wotRkA2e7N7TJLCV6NIHCYB19fhH7s//BnQ2fm1JDv01h1+pdAdHdxm3qErYV+Pk/wtMhu+0ja6ZeTTxtMc67lLGK55+8cPHX9PCcUIA3Xp7peFirwHHsitP0zoNGb8mhdZeZ8t+X0GNKxvEq+NMKPzChVoRhs7pn01TvLln4oIsza75xWq0xZKiqqtbyXx9s146K9lrHPUn6ewzZhADuARxb76gG/j5wde/6/H78/cjRo2xVLij0Gbs997RSSazxeGxpUoYhRIXhMnbkYobaSrHNXUt5o4vftmAtXlJ6YCIhWfUWVcQI6omQOoX14v0RPoZw5pfy4inCy7/kwhfiqq61r0rxFen6G4oLXc0cJFoy6MCokpJvG7qbiwdM/L/MJ6RUxOAzmiSAEqcO3xfE+gIxNKl+1+SfSKDfqvGpl2adDS2u4f/vGPBYxNOVMPaKTnbPXQmEvr2tsFu84GE00IKDR8N9XSyDZV7IyLQQE2Eq+LAPXL6fCeQNc2tJL46Bi70in9PcOXjGAkuutcMNHOQEUHVH/XNdHqrqz8mp/nPGelGBGmrHdlfLUFsAE5ohgBKnDt8ZOeQKPTcHtNeGAWVourfy6z1pKdV/b30xU7T0ce8DlV16rHPncS8QXC22aJ8uwRkDMMGGMCwhi0OoAZYCMa0SEA1C9j6wl0h2jbHdGmtkFjTW+YvK2oZqxnQTUcuhzKYjUgTNN1DYgsVPJOFcZVWsHdfILw5iflhBkb3WqZs9+AGWADchLjAqIA9ctfSQC6qrZlynoXokTP3OgWlchYz1JU1jhqiT271fD9NCsMSesb1eOMmUTx+UJrt8zRywj3liknDJ1k8SaWMV/aUYllRMcC+3VaOC5VkvpVCUC3tfFJFp/RBfRYbJRH4W9LvyU4ba608PU40yW7vD+WqPEOUgyejt+IGMGq5dZPZHWYO6czlSk7ryyMNYniAqISN7JKABoCskQnbBF0j/ozdfv3HXPCWzOUGX6eY8Ngt6N8ZRfUTtNxHUiwsUyZYfe54JZWBtZfASvJVfWAk6hVlQx0THLZnC2Erb3WckfxjUNySiAQHjck9PirzDBkkvmrsA/y54gtJbzj/LWIZctNHFbs82Gkr8OQQJt4fgNwAlGJP5QMNKe6+ZZpwlcE9vggbROfkAL5R1EwW9HYs14H/UQT0jYemXJmh0WFxBSxO1PUFaZscMEIW87sCIVCv9APRMNBYAk4OQQzE5KBbmnlu73OHT6N0IIxNH4r/yiqoLh++V7CRRwlhwdWanxuxS/sA/XTlgoN6L3tvbPlzA7QumlCeMcmsAScRHPHkoGGSiq4C3d4Eb10wpoXDi/lTXd0EtkwVsnh9O1IObPDolDBRCczlBx+mWtz10Lem1odfXImriXc9QksASfRbwmB5rULjhmGE730uykWGFbLuccqLL5kxkY31utAHI5dD5cnL+zK5RXhWpiSw4+znt+Q72JLQHXiZgTJ/AawJAGPEGgIdgzRXAeMzinrXbLz5fIKrlIt9Km+FpqJ8MsceVtoQAW0iNbavp1i4fOGbPhOBjQa9snEKywjplt5BubJk/SUzMr52z1ZrwNxuPQ4Vp68sCuvoHyiEbySQ6fzZfueO+BkEqAimVCftM6FxN7oIAcasn+ZTbJ7a6GeVxuP/oR0bX2rjr4/63XQT9ThWLup9ywHyQhemQEt4KvPz0TJpDaeYPFOwnsngKKdtEU9KUBn5tVg3EoUwbeTLXocgJFJsIT2/h3Ceh3061wsNPMMVOM9dzFJZb8SeENWcliwwzM1m/59F7A3viVe0fvvUoeMXCmbPaUALbbQSTYPHboSSnv+TtjRccOEcPu2MsNPs59HvFXjlUI0K2gaVcGM3mEQ1NRMc6Wwgcs7QnrO45hhBJpw8pdIARp64fse9U0Ux+8L7N6+o+832947m/VqwOeqvdJJnnaFdZVyuGsO+RF5f1NmuPAghvaCW1IGh+jSiX6i+RNHnxypL5EONGxwpJJkn8Ce88G0t3bApEFC2a2DIRPNt/xz9bmaCh3pLdME1q2OQdqmHgE05wmAEIn9CfzO3Y8uLpd+X6Z0oNHIB0UX/TaPsLDQvMWmlEvtCySqpbWdlTNw3QMsM3Pn9Ha+2txEIVFJ6ZxFOwkXwpQT/lps/6GYzkYOwBOXWj521QuiN/86zyYwqpDKDIR0oDtE0xEbj/kTRYYRlcGdqA90t6RYuGSQzKIrIWw7FVjKaaKXeNURjye4+iSezRMrWib618P5tHykfyxpOHM36l/ELll0j/lTPDpACWiBUHjfKonk2N+MjW5+YTQna6KTOnf2sVUTsOAfPU8RCNT7uIpY/uEfh0xibXP5z7Of0/Zy8SrsI8maMcADfhQ3w1ECukN0tGTyOsJFlkHaJmsO+dLLTH1jm41nlqLdfxEF9JKMbIVVBaFHXqTHjtWBXvrkrUh69gYEeEi8sgC8IsrOmagCjTYMhibJlq4hojNeNNxR4hcFRfXL9rCw7Q6mzuk7kZrRPIsF+42Vg1hjVzrFJJfR2NuD2k/KqBxC4Gm/n+jsoNmLdOp1RBXoDtFZw1UHCB2FIey/+KawlM43ioLweVOg5MVb2HxIcJo6z9Z9qfKqpvuWSUp2zdF/tPE9yyRuMx2HLYWljQcuvSF5+ar9PlU1MpzJlwFotL4Pn6eQzN+NXuZo4ZxObxqyrqFt7WE/omuOFBF+nWcL001BDqzYEloyfKIkZ5wVEf5YYJv7sY5GveMnlq4ZJEvRGOE8sE6WqduXAegOkee86bpuRLeC9xONDvOLaHpAxDAWL1daNdw2S+DRmmpUcaH6fUM/KO184U+zn9Pu5WBzz9pEuN2yv5bxVB2X9zI6H5QN6NY2/h3zRJILtr6dbHHLLIH26ayAyMKxq14o2vaAub/7XDCDHiRUTfhQ71ok/aTgFSs0n6DZwiWdT2sQAkjAEsnOjd/m2YIlWdfsZAMaKq9sOnQllCSfMDxo74tA6r2DC1buV+BSC8YfJ25EqMXFuPKoqbndyDaFZM+C/GHiWucn9qn1dO/MjkwsJTE2EA5eelNWKX1psIdkBhqNr3dwPsl8J9rXracC2ukeZmngtgVFF42cQXiRgFw0TzRffcCXcfd8qqnSCu4xw4ifFcM0atnaLbOMIzNwYgGP7acDSbpitDtegfk0enqZge4QTUoACxJLGmM7e+9seabDwuJL1hzyY3bf0iBtU3Pn9Lov3PtpsGB7BEcXkbhYoRfQNtt40Hc2BDCAB8lSGtBadcCX3gE/OkBDrv6500mPA87d6pGZW0Ob6Xa+oKC4/vqz+PGrX8i5iNhfNN+86fhrC5cMhV6poZqCgQsr7sjVsF/m2sjfQMCuPXo9PCmDQ/uuICABMIAHSSxAy5XuzcU0gS6rbILBPph4vhP/dd8qCY/Re79YdQ1tzn65C/W8/lhgS2/Yjk5tuq7r0Wvh73KqGfHoo45C/15c1ojWYfFOLxhdNBoI/OSHmdbTdFzvWyWXlHPl8cgCJAAGOTlAizY5NIFGlvKL6skXWv+90O6uZaL863CwBV+Hf0QbM271C5hWVOoD9P8yxwaDy4uPYnMKapVw9ZjqC1iXVHAfWidvPRnw1yJ7ilNJX48z+88S+22nAp18c1CScl5MCBiABMmmZwRABbRox0MT6A7RfOcL3xzy4vhzkT26J9pRfBZdR0dNXaula8b6w69GLXGAQfxldOhSh062mLTO+dy9aAyi6e1o1XiBqvSc6jvmifO2eXw/3UriJorOFmGuzQI9z7uWSR+KG5jaHQAY/pTmsszxZY48nw19oDtEU4l6Z4K+Jr26auwqJ9p7VoiExgYfcVRimZt/rpVbhoVLup13NizF9PfV1bUtmrQ3Q6ECNo1cXl5hnX/4RyefHDQWGGagkXoTW5yZV8OUP84uAQPAQGYfjjPdbhAoZ/XJBTQUm1yOFpEklQO0TNAYcJm+maVP6iUAAAwGEM+M9RNNnkTLfY2nvECjd7D3zh4505rEtP1tvq1nYB7jtw31SV0Emr0C80kWmAHPyBnWtl5Z8nsAlRfoDtGlbHsuhAwjPXUyXddNCVeF90k19fJNwYxNZJO8w6ZY7j4fLNEjv6xiAGgoM7dmnzQPG+hQaO9b6pP6qqC4ntwoRQA8Uh1uUBQzQLfx+BhJkF8u0enW14zQrW+fNFKobpjO5FOEwCYktlgeF1zdxQzQHaJ7a45cDSPZPIXw61ybs/eia+p60eJzb1ZNfev5BzG/zSM0nfuJnC8CG3pHayWKMaCh9x/qDki7o23oZAsbj6y+ebXeIDuvbPJOG6gAGGDDYKRMAg3lfqxbLu10ID5Zl1e5cvqW7pMqC5Xr7p9LMq0hDkAlV8b9+1LFMNDICb7LEaSbP/uPNp69xT0+raJvBUQjhWpNeFcxd6sHeV8NSIAK4+0aw0B3iO7Me2ybMngC2ZUfyOqinV6pWVV97bSGCRWall21ZJc3Oc3AA5AoYmmCeaCFws4dVUt3e5NvVoR1depW5PsPct0B0CdVE0wIgztR5HMDAAN4ABJF3EbNPNBifSxpWHvIb4i0q5lmbnLHB91ne2iAUInvcqpmS7tjBEgADMXd26sooJE9j4A8Em/s4jBI2/TI1TAUhLr7SuzlQvWlv6/WvxZOdLlgVwASAENxTZiigO4Q7YmLTCyVuko0ZJI5TC75d6X0iUWh+pbufinVsx5gABK0z5tSkQKBhrjN7XZeWUOJHT2JB4hDJ1vsMAhq4PbtXlJLoeL0zgRhUCRlCWKSOWDg0vXvT1GKBVosnzcFY1c6ST0i8d9lDq8jPja1tCtirNAnxoVqQmWhyqR600PVa690AgZKSJUygG4U7YUluYv8n6baZOEOr7C4ksa+plodhGpCZaHKSM7/i8OYFU63zRKUU63KALpD5ObLPSBPqs9cjBEnrXO5aZrA4OJ+nxQhVBCqCZUl8Sxc9zB8aufV3ErzU6UkoDtER7j3XXxD4gZYHAZqm/y5yC44uqhvzUVlhapBBaGaSHzg/9NCmez7O6S2gaZ3JRpSHtAdItvDJ6TTnpZ6cvu7KZYbj/knZ1b2NdUqJVRHSmblxmOvUUHSDMjOy3e8gwuUPNZXKtAYRtQ3tN0ySxgxQ7q/foya0aJn5tX0jRFVRKgIVMf+i2/IFwLFYcR0q1umCeiWlVx9SgVaLGTS0jXjN2lbsfqJrsKdpuPqF/qh7zwi6+I28V6FfZym60rFm/pv82wtXTLqlGhpdIkFoDtEW78vPY6VujAu7rnmb/c0e5He1Ocshj01t7SbO6cv6JzQkFJf/USL25cfx9aw5K2YHaA7RJ5zjWxT/7NYituRrqZ697ngvqMurAho7j4fQvGaC1QoqpVxnx7UxRrQHaLv3i/04/jVL6j4rft6nNmsze6vwj70+fVSmlDUr8I/zt7iToVmVCKqEhXKbgWxCXSHaNQcHF20wyCI5H6t7uHPhXZXn8QnZXD6RooKlfhyKhT1n6R+6LoCqg+ViKpkfVaKZaA7RM1AXEr56oNk92t1D7/MtdE96t9LnJazJRQvCpn65eGoPlSiKnSe7AMtViOXd+hKKPXbVf5YYHfhQUx1HQOuSfrUXTV1LRcfxv57AaWGuZ9onwYqTnV2K6gK0B2iyygeWif/e6Gd1NXUfqKDiZ1bxQ/7eQXlow76LBA5hQJEMXoF568/0nksg8r9eqgmVBaqrIHuNSuKkAoB3dG53ZT3MqRgy4kAik65+2t1en3deOz164hCDb7VStFC0QVEFm46/hqFKXWn0T8lb4xqQmXRu29TcVItoKGmlvbEdM7sze5Szz50BYzB8TxGMKrT8amRUGgoOopTGZ/a5rGmKHBUkwouDqgc0GJxqpuNHdJGUlgh7x6m6bg+tU+tZsLnX28QCgrFhUKTqZBHzLB65pCmsi7dVBToDtFpF8/A/IV6XtSb6gFaJsOmWC7e6W3tnllY1th39laiUCwoHBQRCgrFRe6zuYfRvHCHp2dgnqJPncgj1QW6Q3T0Mr+o/tLjWO2VTtTLvZ/o2it8CUa2qcmZlX3jxS6hKFIyK5/YpaJwviN1f/xlSzFmhePfj2LzC+tV/DizSgMtVnF5o5NPjqw948AxJn8utFu5z8czKJ8Rx8PqLhgYXkH5K/f7dO5jlvFKsakbXB19corKGtnOhHSpAdBiVdU0XzaKoz5w6QoYtv823/amaUJSRiXbmWBH6KZumSWiECjOYHQPKPDLj+Mqa1TUYv5SagO0WBFvSzce8/9jgfStp1+Gr8aa6hkEmTi+S86obGnV8Bs4kUFwbOr0Tu9MEPXlqu4BhYyijnhbwnZWZJOaAc0XCFOzq+6YJ45e5kijtR6obTJypvUGfX87r2w02JU1LTzNuvoN2UGmkDVkENlEZqWekpLYKqN4Ucgoar66DazVDGixeO2C8PiS8/dj/lpsT6Mb7de5d89Ua7njdoNAVFvCu4qKqiYVH+uQC4lHFpARZGf76UBkjfyuPRLzDEWKgkXxqumZTrUEWiyBUBiVWLp870sajVD3MHya1dytHvcskzJyq+W8KVX5QoKRbCQeWZB6qF5q94XCRJHKfxUVi1JjoMUSCIQwrPdfejNulfSzt1KD9kqnY4YRJk7v8M73H+oam3gqVbmdV2U28ZCwiIRSJPK4YYRUbycUWmVjFB0KEFnWgJl7tQe6Q8R0SQXXzitr9mb3n2Y/J3fjKzXg599Ns5y50W3P+RBAExxdlJpV9aG4oaa+lZVeGJEiaiQAyUBiMM7beyEEyRs+zVLW2bcvc4riQqGh6FCAGkBzh2YALRZar/zCelf/3F3ngn+ZYyMn1p964TEm30+zQiu4UM9r19ng68/e2npmhcYVp2ZXlnK4NXUtLa3tDHKAV+GFeC1ejigQEaJDpIgaCUAykBg5Ie5C+Zc5z1FQKC4Umkr1QnJKc4DuEp8vjE0u33jMf/AEmadBqIRB2qbfTbWcvN5l9QHf4zciHtuk+IQUxKdWZObVcKqbYRJQtEHxGB7GT/DD+LQKn5APeNWJGxF4LV6OKKhso6URUCwonJjkMtZPlyhCGgi0WO3tgrdpFfetkuds8fhx1nP5zWupAVH8a7zZ0EnmP8621lrmOGmd84yNbvO2eS7f8xKMIuAP+Cv+Ef+FB/AYHsZPlJO2H2dZz97SOfbFt6dQh7bsSmOB7hAZIS1t/NC4kqtP4xfs8By11IERO0S9ArKMjC/Y7nn1STyKoqWVr0kGxpfSZKC7hL61tIIbmVAKC2HxTm+MhBixRFU5DBQN+JBZZBkZR/Y10sD4Ur0C6O5qbmkPiSnWvxb+8xwbJfT1yg/IFLKGDCKbqnBqVcnqdUB3qba+1TMw78qTuBX7fMatciK/ZkDFAxKPLCAjyA4y1ZtPo/VeoDtE02RNLe1p2VWu/rn618IW6XmNXuY4fJqVgqYXmA1IJJKKBCPZSDyygIwgO5oxnUxbvRro7mrnCzjVzXGp5fbe2YbGb9ce8pu0zgVm6CBtEypHoJUQ+ovcLSNJSBiSh0QiqUgwkq3WG1GYVR/QnwlkNHJ5FVXNqVlVQVFFj21Tdp4NmrvV4z+L7YdMMmfF5kakiBoJQDKQGCQJCUPykEgktQ/lHuoDmpKEwk5/sqnZVU6+OTdMEs7cidphELTu8Kt52zzHr37xxwLbkTOsh02x+HqcqUwzg3gYP8EP8XO8BK/CC/FavBxRICJEB3a5KralRJXVB7RsAlhtPD4IK+M05RXWRyeVeQTkPbFLvWIUd+pW5M6zwRuP+a/a77N098vFu7wX6nkt2OGJlnXOls6AP+Cv+Ef8Fx7AY3gYP8EP8XO8BK/CC/FavBxRIKI+jmVVH9CMSSAU8niCllZ+A7etpq61qrYF1m15VVNpBbe4vDPgD/gr/hH/hQfwGB7GT9R6u6aqqQ/oPmmU/h9V2Bia7bidBwAAAABJRU5ErkJggg=="/> + </defs> + <style/> +<!-- <use id="Background" x="0" y="-185" href="#img1"/>--> + <use id="Layer 1" x="57" y="56" href="#img2"/> + <path id="USACO Guide" fill="#000" + d="M400.39 232.72Q380.38 232.72 369.58 225.23Q358.78 217.6 358.78 197.73L358.78 125.44L380.09 125.44L380.09 197.58Q380.09 206.08 385.13 209.82Q390.31 213.57 400.39 213.57Q410.47 213.57 415.51 209.82Q420.55 206.08 420.55 197.58L420.55 125.44L442.01 125.44L442.01 197.73Q442.01 217.6 431.06 225.23Q420.26 232.72 400.39 232.72ZM491.11 232.86Q482.9 232.86 473.54 231.86Q464.18 230.85 457.7 229.26L457.7 212.42Q470.52 213.71 490.1 213.71Q498.89 213.71 503.35 211.41Q507.82 208.96 507.82 202.91L507.82 199.02Q507.82 194.27 504.94 191.82Q502.2 189.23 497.02 189.23L487.94 189.23Q472.1 189.23 464.18 182.32Q456.41 175.41 456.41 159.71L456.41 154.53Q456.41 124.58 490.82 124.58Q498.89 124.58 507.53 125.73Q516.17 126.74 523.08 128.18L523.08 145.02Q503.35 143.73 493.27 143.73Q485.06 143.73 480.89 146.32Q476.71 148.77 476.71 155.54L476.71 158.42Q476.71 163.89 479.74 166.48Q482.9 168.93 489.24 168.93L499.75 168.93Q513.58 168.93 520.78 176.56Q528.12 184.05 528.12 196.29L528.12 202.34Q528.12 214.43 523.66 221.2Q519.19 227.82 511.13 230.42Q503.06 232.86 491.11 232.86ZM537.05 232L570.46 125.44L597.38 125.44L630.79 232L608.62 232L601.85 209.68L565.56 209.68L558.94 232L537.05 232ZM570.6 190.53L596.81 190.53L583.7 143.73L570.6 190.53ZM676.87 232.86Q663.48 232.86 655.27 228.69Q647.06 224.37 642.6 212.56Q638.28 200.61 638.28 178.43Q638.28 157.12 642.74 145.31Q647.35 133.5 655.7 129.04Q664.06 124.43 677.16 124.43Q686.95 124.43 695.59 126.02Q704.23 127.46 710.42 129.47L710.42 146.03Q706.1 145.02 698.18 144.3Q690.26 143.58 681.62 143.58Q672.98 143.58 668.38 146.32Q663.77 148.91 661.61 156.4Q659.59 163.74 659.59 178.58Q659.59 193.26 661.46 200.61Q663.34 207.95 667.94 210.83Q672.55 213.71 681.62 213.71Q699.19 213.71 710.42 212.27L710.42 228.69Q697.61 232.86 676.87 232.86ZM765.14 232.86Q749.45 232.86 740.09 228.69Q730.73 224.51 725.98 212.85Q721.22 201.18 721.22 178.86Q721.22 156.54 725.98 144.88Q730.87 133.07 740.23 128.75Q749.59 124.43 765.14 124.43Q780.84 124.43 790.06 128.75Q799.42 133.07 804.17 144.88Q808.92 156.54 808.92 178.86Q808.92 201.18 804.17 212.85Q799.42 224.51 790.06 228.69Q780.84 232.86 765.14 232.86ZM765.14 213.71Q774.22 213.71 778.82 211.12Q783.58 208.38 785.59 201.04Q787.61 193.55 787.61 178.86Q787.61 163.6 785.59 156.26Q783.58 148.77 778.82 146.18Q774.22 143.58 765.14 143.58Q756.22 143.58 751.46 146.18Q746.86 148.77 744.7 156.26Q742.54 163.74 742.54 178.86Q742.54 193.7 744.55 201.04Q746.57 208.38 751.18 211.12Q755.93 213.71 765.14 213.71ZM891.72 232.72Q879.05 232.72 871.85 226.24Q864.65 219.76 861.77 208.1Q859.03 196.29 859.03 178.72Q859.03 160.29 862.2 148.62Q865.51 136.96 874.01 130.77Q882.65 124.43 898.34 124.43Q908.57 124.43 916.78 125.73Q924.98 126.88 934.34 129.18L934.34 145.6Q928.73 144.88 919.94 144.3Q911.3 143.58 902.23 143.58Q893.02 143.58 888.41 146.9Q883.8 150.06 882.07 157.55Q880.34 164.9 880.34 178.86Q880.34 192.83 882.07 200.18Q883.8 207.38 887.69 210.26Q891.58 213.14 898.78 213.14Q904.25 213.14 909.58 212.27Q914.9 211.41 918.79 210.11L918.79 189.38L899.64 189.38L899.64 172.53Q903.96 171.95 910.87 171.52Q917.93 171.09 924.26 171.09Q933.19 171.09 939.24 171.52L939.24 232L923.26 232L920.38 224.8Q915.05 227.97 907.42 230.42Q899.93 232.72 891.72 232.72ZM978.55 232.86Q968.04 232.86 962.14 226.67Q956.38 220.48 956.38 209.39L956.38 154.67L977.69 154.67L977.69 203.06Q977.69 209.25 980.42 211.7Q983.3 214 988.92 214Q992.95 214 997.13 212.56Q1001.45 210.98 1004.76 208.38L1004.76 154.67L1026.22 154.67L1026.22 232L1008.5 232L1004.76 224.08Q991.37 232.86 978.55 232.86ZM1044.94 143.3Q1043.5 143.3 1042.63 142.58Q1041.91 141.71 1041.91 140.42L1041.91 126.3Q1041.91 123.28 1044.94 123.28L1061.35 123.28Q1062.65 123.28 1063.37 124.14Q1064.09 125.01 1064.09 126.3L1064.09 140.42Q1064.09 143.3 1061.35 143.3L1044.94 143.3ZM1042.34 232L1042.34 154.67L1063.66 154.67L1063.66 232L1042.34 232ZM1105.42 232.86Q1091.45 232.86 1084.54 223.79Q1077.77 214.72 1077.77 194.13Q1077.77 179.44 1080.36 170.66Q1083.1 161.87 1089.43 157.7Q1095.77 153.52 1106.86 153.52Q1116.36 153.52 1126.58 157.12L1126.58 122.13L1148.04 122.13L1148.04 232L1130.04 232L1126.58 224.66Q1122.55 228.11 1116.79 230.56Q1111.03 232.86 1105.42 232.86ZM1112.9 215.73Q1119.82 215.73 1126.58 211.41L1126.58 172.53Q1122.84 171.09 1119.82 170.37Q1116.94 169.5 1113.77 169.5Q1108.15 169.5 1104.98 171.52Q1101.96 173.39 1100.52 178.58Q1099.08 183.62 1099.08 193.12Q1099.08 202.19 1100.38 207.09Q1101.82 211.84 1104.7 213.86Q1107.72 215.73 1112.9 215.73ZM1196.57 232.72Q1183.46 232.72 1175.98 229.26Q1168.49 225.81 1165.03 217.31Q1161.72 208.82 1161.72 193.41Q1161.72 177.57 1164.89 169.07Q1168.06 160.43 1175.26 156.98Q1182.6 153.52 1195.85 153.52Q1207.66 153.52 1214.42 155.82Q1221.19 158.13 1224.22 163.89Q1227.38 169.65 1227.38 180.16Q1227.38 191.39 1220.9 196.14Q1214.57 200.9 1202.33 200.9L1182.89 200.9Q1183.18 207.09 1184.76 210.54Q1186.34 213.86 1190.38 215.44Q1194.41 216.88 1202.18 216.88L1224.79 216.88L1224.79 229.41Q1217.16 230.99 1210.97 231.86Q1204.78 232.72 1196.57 232.72ZM1182.6 188.22L1200.17 188.22Q1204.63 188.22 1206.5 186.5Q1208.52 184.62 1208.52 180.3Q1208.52 173.97 1205.64 171.52Q1202.76 168.93 1195.85 168.93Q1190.81 168.93 1187.93 170.66Q1185.19 172.24 1183.9 176.42Q1182.6 180.59 1182.6 188.22Z"/> +</svg>
\ No newline at end of file diff --git a/src/components/Confetti/Canvas.js b/src/components/Confetti/Canvas.js new file mode 100644 index 0000000..43d0161 --- /dev/null +++ b/src/components/Confetti/Canvas.js @@ -0,0 +1,46 @@ +import React, { Component } from 'react'; + +class Canvas extends Component { + componentDidMount() { + this.ctx = this.canvas.getContext('2d'); + + this.scale(); + this.props.draw(this.ctx); + } + + componentDidUpdate(prevProps) { + if ( + this.props.width !== prevProps.width || + this.props.height !== prevProps.height + ) + this.scale(); + this.props.draw(this.ctx); + } + + scale = () => { + const ratio = window.devicePixelRatio || 1; + + this.canvas.width = this.props.width * ratio; + this.canvas.height = this.props.height * ratio; + + this.canvas.style.width = this.props.width + 'px'; + this.canvas.style.height = this.props.height + 'px'; + + this.ctx.scale(ratio, ratio); + }; + + render() { + const { width, height, draw, ...delegatedProps } = this.props; + + return ( + <canvas + ref={node => (this.canvas = node)} + width={width} + height={height} + {...delegatedProps} + /> + ); + } +} + +export default Canvas; diff --git a/src/components/Confetti/Confetti.helpers.ts b/src/components/Confetti/Confetti.helpers.ts new file mode 100644 index 0000000..e759619 --- /dev/null +++ b/src/components/Confetti/Confetti.helpers.ts @@ -0,0 +1,59 @@ +const hexRegex = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i; + +type HexColor = string; +type RGBColor = { + r: number; + g: number; + b: number; + a: number; +}; + +export const random = (low: number, high: number) => + Math.random() * (high - low) + low; + +export const sample = (array: Array<any>): any => + array[Math.floor(random(0, array.length))]; + +export const range = (n: number) => Array.from(Array(n).keys()); + +export const getDiameter = (width: number, height: number) => + Math.sqrt(width * width + height * height); + +export const convertHexColorToRgb = (color: HexColor): RGBColor => { + const match = color.match(hexRegex); + if (match) { + return { + r: parseInt(match[1], 16) / 255, + g: parseInt(match[2], 16) / 255, + b: parseInt(match[3], 16) / 255, + a: 1, + }; + } else { + throw new Error(`Failed to parse color: ${color}`); + } +}; + +export const mix = (color: RGBColor, background: RGBColor) => ({ + r: color.r * color.a + background.r * (1 - color.a), + g: color.g * color.a + background.g * (1 - color.a), + b: color.b * color.a + background.b * (1 - color.a), + a: 1, +}); + +export const mixWithBlack = (color: RGBColor, amount: number) => { + const black = { r: 0, g: 0, b: 0, a: amount }; + + return mix(black, color); +}; + +export const mixWithWhite = (color: RGBColor, amount: number) => { + const white = { r: 1, g: 1, b: 1, a: amount }; + + return mix(white, color); +}; + +// Stringify the color in an `rgba()` format. +export const formatRgbColor = (color: RGBColor) => + 'rgba(' + + `${Math.floor(color.r * 255)}, ${Math.floor(color.g * 255)}, ` + + `${Math.floor(color.b * 255)}, ${color.a.toFixed(2)})`; diff --git a/src/components/Confetti/Confetti.js b/src/components/Confetti/Confetti.js new file mode 100644 index 0000000..406c158 --- /dev/null +++ b/src/components/Confetti/Confetti.js @@ -0,0 +1,307 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { random, sample, range, getDiameter } from './Confetti.helpers'; +import { createCircle, createTriangle, createZigZag } from './confetti-shapes'; + +import Canvas from './Canvas'; + +class Confetti extends Component { + static propTypes = { + // The width of the HTML canvas. + width: PropTypes.number.isRequired, + // The height of the HTML canvas. + height: PropTypes.number.isRequired, + + // An array of shapes, used when generating particles + // (each item provided has an equal chance of being selected as a + // particle) + shapes: PropTypes.array, + + // When do we want to invoke "generateParticles"? + // NOTE: a better implementation would take an array. Doing it the simplest + // way for the demo, but a more fleshed-out implementation would be: + // PropTypes.arrayOf(PropTypes.oneOf(['click', 'mount', 'hover'])) + makeItRainOn: PropTypes.oneOf(['click', 'mount']), + + // The number of particles to generate, spread over the + // `emitDuration` length. + numParticles: PropTypes.number, + // Amount of time to spread the release of particles, in milliseconds. + emitDuration: PropTypes.number, + + // The amount of downward acceleration to provide. + // Range: 10 = very slow, 10,000 = very fast. + gravity: PropTypes.number, + // The amount of Z-axis (2D) rotation to provide to each particle. + // Each particle has a random number specified, between 0 and n. + // Range: 0 = no spin, 10 = reasonable spin, 100 = pukeville + spin: PropTypes.number, + // The amount of X-axis (3D) rotation to provide to each particle. + // Each particle has a random number specified, between 0 and n. + // Range: 0 = no twist, 10 = reasonable twist, 100 = hummingbird + twist: PropTypes.number, + // Each particle will have a random speed assigned, contained by + // `minSpeed` and `maxSpeed`. + // This is the base speed, which is affected by `gravity`. + minSpeed: PropTypes.number, + maxSpeed: PropTypes.number, + // Each particle will have a random size applied, contained by + // `minScale` and `maxScale`. If you'd like all particles to retain + // their original size, simply provide `1` for both values. + minScale: PropTypes.number, + maxScale: PropTypes.number, + + // Callback triggered when animation ends. + // NOTE: Only fires when all particles are off-canvas. Retriggering + // the confetti before it's completed will delay the handler. + onComplete: PropTypes.func, + }; + + static defaultProps = { + shapes: [ + createZigZag({ fill: '#ca337c' }), // Pink + createZigZag({ fill: '#01d1c1' }), // Turquoise + createZigZag({ fill: '#f4d345' }), // Yellow + createCircle({ fill: '#63d9ea' }), // Blue + createCircle({ fill: '#ed5fa6' }), // Pink + createCircle({ fill: '#aa87ff' }), // Purple + createCircle({ fill: '#26edd5' }), // Turquoise + createTriangle({ fill: '#ed5fa6' }), // Pink + createTriangle({ fill: '#aa87ff' }), // Purple + createTriangle({ fill: '#26edd5' }), // Turquoise + ], + numParticles: 80, + // By default emit all particles at once. + emitDuration: 0, + gravity: 1600, + spin: 20, + twist: 0, + minSpeed: 225, + maxSpeed: 675, + minScale: 0.4, + maxScale: 1.0, + }; + + state = { + particles: [], + }; + + componentDidMount() { + if (this.props.makeItRainOn === 'mount') { + this.generateParticles(); + } + } + + componentDidUpdate(prevProps, prevState) { + if (prevState.particles.length === 0 && this.state.particles.length > 0) { + this.tick(); + } + } + + componentWillUnmount() { + window.cancelAnimationFrame(this.animationFrameId); + } + + generateParticles = () => { + const newParticles = range(this.props.numParticles).map(i => { + // Particles can be spread over a duration. + // Each particle should be "born" at a random time during the emit + // duration (if this value is 0, they'll all share the same birthdate). + const birth = Date.now() + random(0, this.props.emitDuration); + + // Scale and Speed are specified in ranges. Select a value for this + // particle from within the range. + const speed = random(this.props.minSpeed, this.props.maxSpeed); + const scale = random(this.props.minScale, this.props.maxScale); + + // Values for `spin` and `twist` are specified through props, but the + // values given represent the maximum absolute values possible. + // If `spin` is 30, that means each particle will select a random + // `spinForce` between -30 and 30. + const spinForce = this.props.spin * random(-1, 1); + const twistForce = this.props.twist * random(-1, 1); + // `currentSpin` and `currentTwist` are trackers for the current values + // as the animation progresses. Start them at `0`. + // NOTE: this does not mean that each particle will start with the same + // orientation - this is also influenced by `angle`, which is randomized. + // This represents the current deviation from the `angle`. + const currentSpin = 0; + const currentTwist = 0; + + // Each particle starts along the top of the canvas, with a random + // `x` coordinate somewhere along its width. + const initialPosition = { + x: random(0, 1) * this.props.width, + y: 0, + }; + + const shape = sample(this.props.shapes); + const { front, back } = shape; + + // ~~~ TRIGONOMETRY STUFF ~~~ + // `angle` is the degree, in radians, that the shape is rotated along + // its Z-axis: + // ________ + // /\ π radians \ / + // / \ -> \ / + // /______\ \/ + // + const angle = random(0, 2 * Math.PI); + + // `trajectory` represents the angle of the particle's movement. + // Larger numbers means the particle deviates further from its initial + // `x` coordinate. + const trajectoryVariance = random(-1, 1); + const trajectory = -Math.PI / 2 + trajectoryVariance; + + // `vx` and `vy` represent the velocity across both axes, and will be + // used to compute how much a particle should move in a given frame. + const vx = Math.cos(trajectory) * speed; + const vy = Math.sin(trajectory) * speed * -1; + // + // ~~~ END TRIGONOMETRY STUFF ~~~ + + return { + birth, + initialPosition, + currentPosition: initialPosition, + spinForce, + twistForce, + currentSpin, + currentTwist, + angle, + scale, + vx, + vy, + front, + back, + width: front.naturalWidth, + height: front.naturalHeight, + }; + }); + + this.setState({ + particles: [...this.state.particles, ...newParticles], + }); + }; + + tick = () => { + if (this.state.particles.length === 0) { + return; + } + + this.animationFrameId = window.requestAnimationFrame(() => { + const particles = this.calculateNextPositionForParticles(); + + this.setState({ particles }, this.tick); + }); + }; + + calculateNextPositionForParticles = () => { + const now = Date.now(); + const { height, width } = this.props; + + return this.state.particles + .map(particle => { + const age = (now - particle.birth) / 1000; + + // Skip a particle if it hasn't been born yet. + if (age < 0) { + return particle; + } + + const x = particle.initialPosition.x + particle.vx * age; + const y = + particle.initialPosition.y + + particle.vy * age + + (this.props.gravity * age * age) / 2; + + const diameter = getDiameter( + particle.width * particle.scale, + particle.height * particle.scale + ); + + const isOffScreen = + x + diameter < 0 || x - diameter > width || y - diameter > height; + + if (isOffScreen) { + return null; + } + + // WARNING WARNING WARNING + // Mutating this.state directly here. + // This is a faux-pas, but it's important for performance. + particle.currentPosition = { x, y }; + particle.currentSpin = particle.angle + particle.spinForce * age; + particle.currentTwist = particle.twistForce + ? Math.cos(particle.angle + particle.twistForce * age) + : 1; + + return particle; + }) + .filter(particle => !!particle); + }; + + draw = ctx => { + const { width, height } = this.props; + const { particles } = this.state; + + ctx.clearRect(0, 0, width, height); + + particles.forEach(particle => { + // Apply its new position, in terms of cartesian coordinates. + ctx.translate(particle.currentPosition.x, particle.currentPosition.y); + + // Apply its new Z-axis (2D) rotation (how much spin is currently + // applied?) + ctx.rotate(particle.currentSpin); + + // Apply its new X-axis (3D rotation), and figure out whether it's + // "backwards" right now. + const imageElem = + particle.currentTwist >= 0 ? particle.front : particle.back; + + ctx.scale(particle.currentTwist, 1); + + // Draw the image into the context, applying the right scale and + // position so that it's in the right place. + ctx.drawImage( + imageElem, + imageElem.naturalWidth * particle.scale * -0.5, + imageElem.naturalHeight * particle.scale * -0.5, + imageElem.naturalWidth * particle.scale, + imageElem.naturalHeight * particle.scale + ); + + // Undo all of our transformations, so that our context is restored. + const ratio = window.devicePixelRatio || 1; + + ctx.setTransform(ratio, 0, 0, ratio, 0, 0); + }); + }; + + render() { + const { width, height, onClick, makeItRainOn } = this.props; + + return ( + <Canvas + width={width} + height={height} + draw={this.draw} + ref={node => (this.canvas = node)} + onClick={ev => { + if (makeItRainOn === 'click') { + this.generateParticles(); + } + + if (typeof onClick === 'function') { + onClick(ev); + } + }} + /> + ); + } +} + +export default Confetti; diff --git a/src/components/Confetti/Particles.tsx b/src/components/Confetti/Particles.tsx new file mode 100644 index 0000000..4b454a5 --- /dev/null +++ b/src/components/Confetti/Particles.tsx @@ -0,0 +1,252 @@ +import * as React from 'react'; + +import { random, sample, range, getDiameter } from './Confetti.helpers'; +import { defaultShapes } from './confetti-shapes.js'; + +import type { Particle, Shape } from './types'; + +type State = { + particles: Array<Particle>; +}; + +type ChildFnProps = { + particles: Array<Particle>; + generateParticles: () => void; +}; + +type Props = { + // The width of the HTML canvas. No default value provided. + width: number; + // The height of the HTML canvas. No default value provided. + height: number; + + // An array of shapes, used when generating particles + // (each item provided has an equal chance of being selected as a + // particle) + shapes: Array<Shape>; + + // The number of particles to generate, spread over the + // `emitDuration` length. + numParticles: number; + + // The amount of downward acceleration to provide. + // Range: 10 = very slow, 10,000 = very fast. + gravity: number; + // The amount of Z-axis (2D) rotation to provide to each particle. + // Each particle has a random number specified, between 0 and n. + // Range: 0 = no spin, 10 = reasonable spin, 100 = pukeville + spin: number; + // The amount of X-axis (3D) rotation to provide to each particle. + // Each particle has a random number specified, between 0 and n. + // Range: 0 = no twist, 10 = reasonable twist, 100 = hummingbird + twist: number; + + // Each particle will have a random speed assigned, contained by + // `minSpeed` and `maxSpeed`. + // This is the base speed, which is affected by `gravity`. + minSpeed: number; + maxSpeed: number; + // Each particle will have a random size applied, contained by + // `minScale` and `maxScale`. If you'd like all particles to retain + // their original size, simply provide `1` for both values. + minScale: number; + maxScale: number; + + // Amount of time to spread the release of particles, in milliseconds. + emitDuration: number; + + // Callback triggered when animation ends. + // NOTE: Only fires when all particles are off-canvas. Retriggering + // the confetti before it's completed will delay the handler. + onComplete?: () => void; + + // This component simply creates and updates the Particles. + // The children are responsible for figuring out what to do with them. + children: (props: ChildFnProps) => React.ReactNode; +}; + +class Particles extends React.PureComponent<Props, State> { + static defaultProps = { + shapes: defaultShapes, + numParticles: 100, + gravity: 1000, + spin: 10, + twist: 5, + minSpeed: 225, + maxSpeed: 575, + minScale: 1, + maxScale: 1.5, + emitDuration: 1000, + }; + + state = { + particles: [], + }; + + animationFrameId: number; + + componentDidUpdate(prevProps: Props, prevState: State) { + if (prevState.particles.length === 0 && this.state.particles.length > 0) { + this.tick(); + } + } + + componentWillUnmount() { + window.cancelAnimationFrame(this.animationFrameId); + } + + generateParticles = () => { + const newParticles = range(this.props.numParticles).map(i => { + // Particles can be spread over a duration. + // Each particle should be "born" at a random time during the emit + // duration (if this value is 0, they'll all share the same birthdate). + const birth = Date.now() + random(0, this.props.emitDuration); + + // Scale and Speed are specified in ranges. Select a value for this + // particle from within the range. + const speed = random(this.props.minSpeed, this.props.maxSpeed); + const scale = random(this.props.minScale, this.props.maxScale); + + // Values for `spin` and `twist` are specified through props, but the + // values given represent the maximum absolute values possible. + // If `spin` is 30, that means each particle will select a random + // `spinForce` between -30 and 30. + const spinForce = this.props.spin * random(-1, 1); + const twistForce = this.props.twist * random(-1, 1); + // `currentSpin` and `currentTwist` are trackers for the current values + // as the animation progresses. Start them at `0`. + // NOTE: this does not mean that each particle will start with the same + // orientation - this is also influenced by `angle`, which is randomized. + // This represents the current deviation from the `angle`. + const currentSpin = 0; + const currentTwist = 0; + + // Each particle starts along the top of the canvas, with a random + // `x` coordinate somewhere along its width. + const initialPosition = { + x: random(0, 1) * this.props.width, + y: 0, + }; + + const shape = sample(this.props.shapes); + const { front, back } = shape; + + // ~~~ TRIGONOMETRY STUFF ~~~ + // `angle` is the degree, in radians, that the shape is rotated along + // its Z-axis: + // ________ + // /\ π radians \ / + // / \ -> \ / + // /______\ \/ + // + const angle = random(0, 2 * Math.PI); + + // `trajectory` represents the angle of the particle's movement. + // Larger numbers means the particle deviates further from its initial + // `x` coordinate. + const trajectoryVariance = random(-1, 1); + const trajectory = -Math.PI / 2 + trajectoryVariance; + + // `vx` and `vy` represent the velocity across both axes, and will be + // used to compute how much a particle should move in a given frame. + const vx = Math.cos(trajectory) * speed; + const vy = Math.sin(trajectory) * speed * -1; + // + // ~~~ END TRIGONOMETRY STUFF ~~~ + + return { + birth, + initialPosition, + currentPosition: initialPosition, + spinForce, + twistForce, + currentSpin, + currentTwist, + angle, + scale, + vx, + vy, + front, + back, + width: front.naturalWidth, + height: front.naturalHeight, + }; + }); + + this.setState({ + particles: [...this.state.particles, ...newParticles], + }); + }; + + tick = () => { + if (this.state.particles.length === 0) { + return; + } + + this.animationFrameId = window.requestAnimationFrame(() => { + const particles = this.calculateNextPositionForParticles(); + + this.setState({ particles }, this.tick); + }); + }; + + calculateNextPositionForParticles = () => { + const now = Date.now(); + const { height, width } = this.props; + + return this.state.particles + .map(particle => { + const age = (now - particle.birth) / 1000; + + // Skip a particle if it hasn't been born yet. + if (age < 0) { + return particle; + } + + const x = particle.initialPosition.x + particle.vx * age; + const y = + particle.initialPosition.y + + particle.vy * age + + (this.props.gravity * age * age) / 2; + + const diameter = getDiameter( + particle.width * particle.scale, + particle.height * particle.scale + ); + + const isOffScreen = + x + diameter < 0 || x - diameter > width || y - diameter > height; + + if (isOffScreen) { + return null; + } + + // WARNING WARNING WARNING + // Mutating this.state directly here. + // This is a faux-pas, but it's important for performance. + particle.currentPosition = { x, y }; + particle.currentSpin = particle.angle + particle.spinForce * age; + particle.currentTwist = particle.twistForce + ? Math.cos(particle.angle + particle.twistForce * age) + : 1; + + return particle; + }) + .filter(particle => !!particle); + }; + + render() { + const { children } = this.props; + const { particles } = this.state; + + return children({ + // State + particles, + + // Actions + generateParticles: this.generateParticles, + }); + } +} + +export default Particles; diff --git a/src/components/Confetti/confetti-shapes.tsx b/src/components/Confetti/confetti-shapes.tsx new file mode 100644 index 0000000..9a40853 --- /dev/null +++ b/src/components/Confetti/confetti-shapes.tsx @@ -0,0 +1,171 @@ +import { + convertHexColorToRgb, + mixWithBlack, + mixWithWhite, + formatRgbColor, +} from './Confetti.helpers'; + +// Creates an actual <img> element. This is needed to paint a shape into an +// HTML canvas. +const createImageElement = svgString => { + if (typeof window === 'undefined') return null; + + // window.btoa creates a base64 encoded string. Combined with the data + // prefix, it can be used as an image `src`. + const base64ShapeString = + 'data:image/svg+xml;base64,' + window.btoa(svgString); + + const imageElement = new Image(); + imageElement.src = base64ShapeString; + + return imageElement; +}; + +// Each of the following shape factories returns a string representation of +// an SVG, which can be used to create an <img> element. +// TODO (josh): See if there's a way to use React components instead, just +// need to find a client-side way to get a string representation of a +// component's rendered result. +const circleShapeFactory = ({ size = 15, fill }) => ` + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 10 10" + width="${size}" + height="${size}" + > + <circle + cx="5" + cy="5" + r="5" + fill="${formatRgbColor(fill)}" + /> + </svg> +`; + +const triangleShapeFactory = ({ size = 16, fill }) => ` + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 10 10" + width="${size}" + height="${size}" + > + <polygon + fill="${formatRgbColor(fill)}" + points="0 10 5 0 10 10" + /> + </svg> +`; + +const rectangleShapeFactory = ({ width = 6, height = 12, fill }) => ` + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 ${width} ${height}" + width="${width}" + height="${height}" + > + <rect + x="0" + y="0" + width="${width}" + height="${height}" + fill="${formatRgbColor(fill)}" + /> + </svg> +`; + +const zigZagShapeFactory = ({ size = 20, fill }) => ` + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 23.74 92.52" + width="${size}" + height="${size * 4}" + > + <polygon + fill="${formatRgbColor(fill)}" + points="17.08 31.49 3.56 29.97 10.22 0 23.74 1.52 17.08 31.49" + /> + <polygon + fill="${formatRgbColor(fill)}" + points="13.53 92.52 0 91 6.66 61.03 20.19 62.55 13.53 92.52" + /> + <polygon + fill="${formatRgbColor(mixWithWhite(fill, 0.35))}" + points="20.19 62.55 6.66 61.03 3.56 29.97 17.08 31.49 20.19 62.55" + /> + </svg> +`; + +type ShapeProps = { + fill?: string; + backsideDarkenAmount?: number; + size?: number; + width?: number; + height?: number; +}; + +// Our base export, this generalized helper is used to create image tags +// from the props provided. +// NOTE: You probably want to use one of the preloaded helpers below +// (eg. createCircle). +export const createShape = (shape: string) => ({ + fill = '#000000', + backsideDarkenAmount = 0.25, + ...args +}: ShapeProps = {}) => { + // Convert fill to RGB + const fillRgb = convertHexColorToRgb(fill); + + // Get the factory for the provided shape + let shapeFactory; + switch (shape) { + case 'circle': { + shapeFactory = circleShapeFactory; + break; + } + case 'triangle': { + shapeFactory = triangleShapeFactory; + break; + } + case 'rectangle': { + shapeFactory = rectangleShapeFactory; + break; + } + case 'zigZag': { + shapeFactory = zigZagShapeFactory; + break; + } + default: + throw new Error('Unrecognized shape passed to `createShape`'); + } + + // Create a front and back side, where the back is identical but with a + // darker colour. + const backColor = mixWithBlack(fillRgb, backsideDarkenAmount); + + const frontSvgString = shapeFactory({ fill: fillRgb, ...args }); + const backSvgString = shapeFactory({ fill: backColor, ...args }); + + // Create and return image elements for both sides. + return { + front: createImageElement(frontSvgString), + back: createImageElement(backSvgString), + }; +}; + +export const createCircle = createShape('circle'); +export const createTriangle = createShape('triangle'); +export const createRectangle = createShape('rectangle'); +export const createZigZag = createShape('zigZag'); + +export const defaultShapes = [ + createZigZag({ fill: '#ca337c' }), // Pink + createZigZag({ fill: '#01d1c1' }), // Turquoise + createZigZag({ fill: '#f4d345' }), // Yellow + createCircle({ fill: '#63d9ea' }), // Blue + createCircle({ fill: '#ed5fa6' }), // Pink + createCircle({ fill: '#aa87ff' }), // Purple + createCircle({ fill: '#26edd5' }), // Turquoise + createTriangle({ fill: '#ed5fa6' }), // Pink + createTriangle({ fill: '#aa87ff' }), // Purple + createTriangle({ fill: '#26edd5' }), // Turquoise +]; diff --git a/src/components/Confetti/index.ts b/src/components/Confetti/index.ts new file mode 100644 index 0000000..c5e4840 --- /dev/null +++ b/src/components/Confetti/index.ts @@ -0,0 +1,3 @@ +/* Source: https://github.com/joshwcomeau/react-europe-talk-2018 */ + +export { default } from './Confetti'; diff --git a/src/components/Confetti/types.ts b/src/components/Confetti/types.ts new file mode 100644 index 0000000..cea91c5 --- /dev/null +++ b/src/components/Confetti/types.ts @@ -0,0 +1,22 @@ +export type Particle = { + birth: number; + initialPosition: [number, number]; + currentPosition: [number, number]; + spinForce: number; + twistForce: number; + currentSpin: number; + currentTwist: number; + angle: number; + scale: number; + vx: number; + vy: number; + front: HTMLElement; + back: HTMLElement; + width: number; + height: number; +}; + +export type Shape = { + front: HTMLImageElement; + back: HTMLImageElement; +}; diff --git a/src/components/ContactUsSlideover/ContactUsSlideover.tsx b/src/components/ContactUsSlideover/ContactUsSlideover.tsx new file mode 100644 index 0000000..ac7f223 --- /dev/null +++ b/src/components/ContactUsSlideover/ContactUsSlideover.tsx @@ -0,0 +1,325 @@ +import * as React from 'react'; +import { ModuleInfo } from '../../models/module'; +import { SECTION_LABELS } from '../../../content/ordering'; +import SlideoverForm from './SlideoverForm'; +import { useState } from 'react'; +import useStickyState from '../../hooks/useStickyState'; + +// Warning: this file is insanely messy. This should be rewritten soon :) + +const Field = ({ label, id, value, onChange, errorMsg = null }) => { + return ( + <div className="space-y-1"> + <label + htmlFor={id} + className="block text-sm font-medium leading-5 text-gray-900" + > + {label} + </label> + <div className="relative rounded-md shadow-sm"> + <input + id={id} + className={ + 'form-input block w-full sm:text-sm sm:leading-5 transition ease-in-out duration-150 ' + + (errorMsg + ? 'pr-10 border-red-300 text-red-900 placeholder-red-300 focus:border-red-300 focus:shadow-outline-red' + : '') + } + value={value} + onChange={onChange} + /> + {errorMsg && ( + <div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none"> + <svg + className="h-5 w-5 text-red-500" + fill="currentColor" + viewBox="0 0 20 20" + > + <path + fillRule="evenodd" + d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" + clipRule="evenodd" + /> + </svg> + </div> + )} + </div> + {errorMsg && <p className="mt-2 text-sm text-red-600">{errorMsg}</p>} + </div> + ); +}; + +function validateEmail(email) { + const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +} + +export default function ContactUsSlideover({ + isOpen, + onClose, + activeModule, +}: { + isOpen: boolean; + onClose: any; + activeModule: ModuleInfo; +}) { + const [name, setName] = useStickyState('', 'contact_form_name'); + const [email, setEmail] = useStickyState('', 'contact_form_email'); + const [location, setLocation] = useState(''); + const [topic, setTopic] = useStickyState('', 'contact_form_topic'); + const topics = [ + 'Typo / Broken Link', + 'Unclear Explanation', + 'Website Bug', + 'Request - Missing Section', + 'Request - Problem Editorial', + 'Suggestion', + 'Other', + ]; + const [message, setMessage] = useStickyState('', 'contact_form_message'); + const [showSuccess, setShowSuccess] = useState(false); + const [submitEnabled, setSubmitEnabled] = useState(true); + const [showErrors, setShowErrors] = useState(false); + + React.useEffect(() => { + if (activeModule) + setLocation( + `${activeModule.title} - ${SECTION_LABELS[activeModule.section]}` + ); + else setLocation(''); + }, [activeModule]); + + React.useEffect(() => { + if (isOpen) { + setShowSuccess(false); + setShowErrors(false); + setSubmitEnabled(true); + } + }, [isOpen]); + + const handleSubmit = async e => { + e.preventDefault(); + + setShowErrors(true); + if ( + name === '' || + email === '' || + !validateEmail(email) || + message === '' + ) { + return; + } + + let data = new FormData(); + data.append('name', name); + data.append('email', email); + data.append('location', location); + data.append('topic', topic); + data.append('message', message); + setSubmitEnabled(false); + try { + await fetch( + 'https://formsubmit.co/ajax/da14a047686367521c3c8c5a79b03c97', + { + method: 'POST', + mode: 'no-cors', + body: data, + } + ); + setTopic(''); + setMessage(''); + setShowSuccess(true); + } catch (e) { + setSubmitEnabled(true); + alert('Form submission failed: ' + e.message); + } finally { + setShowErrors(false); + } + }; + + return ( + <SlideoverForm + isOpen={isOpen} + onClose={onClose} + title="Contact Us" + subtitle="Contact us about anything: suggestions, bugs, assistance, and more!" + footerButtons={ + <> + <span className="inline-flex rounded-md shadow-sm"> + <button + type="button" + className="py-2 px-4 border border-gray-300 rounded-md text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition duration-150 ease-in-out" + onClick={onClose} + > + Cancel + </button> + </span> + <span className="inline-flex rounded-md shadow-sm"> + <button + type="submit" + disabled={!submitEnabled} + className={`inline-flex justify-center py-2 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white transition duration-150 ease-in-out ${ + submitEnabled + ? 'bg-blue-600 hover:bg-blue-500 focus:outline-none focus:border-blue-700 focus:shadow-outline-blue active:bg-blue-700' + : 'bg-blue-400 focus:outline-none cursor-default' + }`} + > + Contact Us + </button> + </span> + </> + } + onSubmit={handleSubmit} + > + <div className="px-4 sm:px-6"> + {showSuccess && ( + <div className="rounded-md bg-green-50 p-4 mt-6"> + <div className="flex"> + <div className="flex-shrink-0"> + <svg + className="h-5 w-5 text-green-400" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="ml-3"> + <h3 className="text-sm leading-5 font-medium text-green-800"> + Message received! + </h3> + <div className="mt-2 text-sm leading-5 text-green-700"> + <p> + We will try our best to respond (if one is needed) within a + week. + </p> + <p className="pt-2"> + For urgent requests, please feel free to email{' '} + <a + href="mailto:nathan.r.wang@gmail.com" + className="underline text-blue-600" + > + nathan.r.wang@gmail.com + </a> + . + </p> + </div> + </div> + </div> + </div> + )} + {!showSuccess && ( + <div className="space-y-6 pt-6 pb-5"> + <Field + label="Name" + id="contact_name" + value={name} + onChange={e => setName(e.target.value)} + errorMsg={ + showErrors && name === '' ? 'This field is required.' : null + } + /> + <Field + label="Email" + id="contact_email" + value={email} + onChange={e => setEmail(e.target.value)} + errorMsg={ + showErrors + ? email === '' + ? 'This field is required.' + : !validateEmail(email) + ? 'Please enter a valid email address.' + : null + : null + } + /> + <Field + label="Module (If Applicable)" + id="contact_module" + value={location} + onChange={e => setLocation(e.target.value)} + /> + <fieldset className="space-y-2"> + <legend className="text-sm leading-5 font-medium text-gray-900"> + Topic + </legend> + <div className="space-y-3"> + {topics.map((t, idx) => ( + <div key={idx}> + <div className="relative flex items-start"> + <div className="absolute flex items-center h-5"> + <input + id={`contact_topic_${idx}`} + type="radio" + name="type" + className="form-radio h-4 w-4 text-blue-600 transition duration-150 ease-in-out" + checked={topic === t} + onChange={() => setTopic(t)} + /> + </div> + <div className="pl-7 text-sm leading-5"> + <label + htmlFor={`contact_topic_${idx}`} + className="font-medium text-gray-900" + > + {t} + </label> + </div> + </div> + </div> + ))} + </div> + </fieldset> + <div className="space-y-1"> + <label + htmlFor="contact_message" + className="block text-sm font-medium leading-5 text-gray-900" + > + Message + </label> + <div className="relative rounded-md shadow-sm"> + <textarea + id="contact_message" + rows={4} + className={ + 'form-input block w-full sm:text-sm sm:leading-5 transition ease-in-out duration-150 ' + + (showErrors && message === '' + ? 'border-red-300 text-red-900 placeholder-red-300 focus:border-red-300 focus:shadow-outline-red' + : '') + } + value={message} + onChange={e => setMessage(e.target.value)} + /> + {showErrors && message === '' && ( + <div className="absolute top-0 pt-2 right-0 pr-3 flex items-center pointer-events-none"> + <svg + className="h-5 w-5 text-red-500" + fill="currentColor" + viewBox="0 0 20 20" + > + <path + fillRule="evenodd" + d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" + clipRule="evenodd" + /> + </svg> + </div> + )} + </div> + {showErrors && message === '' && ( + <p className="mt-2 text-sm text-red-600"> + This field is required. + </p> + )} + </div> + </div> + )} + </div> + </SlideoverForm> + ); +} diff --git a/src/components/ContactUsSlideover/SlideoverForm.tsx b/src/components/ContactUsSlideover/SlideoverForm.tsx new file mode 100644 index 0000000..45d149b --- /dev/null +++ b/src/components/ContactUsSlideover/SlideoverForm.tsx @@ -0,0 +1,97 @@ +import * as React from 'react'; +import Transition from '../Transition'; +import { FormEvent } from 'react'; + +type SlideoverProps = { + isOpen: boolean; + onClose: Function; + title: React.ReactNode; + subtitle: React.ReactNode; + children: React.ReactNode; + footerButtons: React.ReactNode; + onSubmit: (event: FormEvent<HTMLFormElement>) => void; +}; + +export default function SlideoverForm(props: SlideoverProps) { + return ( + <Transition show={props.isOpen} timeout={700}> + <div className="fixed inset-0 overflow-hidden z-10"> + <div className="absolute inset-0 overflow-hidden"> + <Transition + enter="ease-in-out duration-500" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="ease-in-out duration-500" + leaveFrom="opacity-100" + leaveTo="opacity-0" + > + <div + className="absolute inset-0 bg-gray-500 bg-opacity-75 transition-opacity" + onClick={() => props.onClose()} + /> + </Transition> + + <section className="absolute inset-y-0 max-w-full right-0 flex"> + <Transition + enter="transform transition ease-in-out duration-500 sm:duration-700" + enterFrom="translate-x-full" + enterTo="translate-x-0" + leave="transform transition ease-in-out duration-500 sm:duration-700" + leaveFrom="translate-x-0" + leaveTo="translate-x-full" + > + <div className="w-screen max-w-md"> + <form + className="h-full divide-y divide-gray-200 flex flex-col bg-white shadow-xl" + onSubmit={props.onSubmit} + > + <div className="flex-1 h-0 overflow-y-auto"> + <header className="space-y-1 py-6 px-4 bg-blue-700 sm:px-6"> + <div className="flex items-center justify-between space-x-3"> + <h2 className="text-lg leading-7 font-medium text-white"> + {props.title} + </h2> + <div className="h-7 flex items-center"> + <button + aria-label="Close panel" + className="text-blue-200 hover:text-white" + onClick={() => props.onClose()} + > + <svg + className="h-6 w-6" + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + d="M6 18L18 6M6 6l12 12" + /> + </svg> + </button> + </div> + </div> + <div> + <p className="text-sm leading-5 text-blue-300"> + {props.subtitle} + </p> + </div> + </header> + <div className="flex-1 flex flex-col justify-between"> + {props.children} + </div> + </div> + <div className="flex-shrink-0 px-4 py-4 space-x-4 flex justify-end"> + {props.footerButtons} + </div> + </form> + </div> + </Transition> + </section> + </div> + </div> + </Transition> + ); +} diff --git a/src/components/Dashboard/ActiveItems.tsx b/src/components/Dashboard/ActiveItems.tsx new file mode 100644 index 0000000..59003f5 --- /dev/null +++ b/src/components/Dashboard/ActiveItems.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import { Link } from 'gatsby'; + +type ActiveItemStatus = 'Solving' | 'Skipped' | 'In Progress'; + +export type ActiveItem = { + label: string; + status: ActiveItemStatus; + url: string; +}; + +const statusClasses: { [key in ActiveItemStatus]: string } = { + Solving: 'bg-yellow-100 text-yellow-800', + Skipped: 'bg-gray-100 text-gray-800', + 'In Progress': 'bg-green-100 text-green-800', +}; + +export default function ActiveItems({ + type, + items, +}: { + type: 'problems' | 'modules'; + items: ActiveItem[]; +}) { + return ( + <div className="bg-white shadow sm:rounded-lg mb-8"> + <div className="px-4 py-5 sm:p-6"> + <h3 className="text-lg leading-6 font-medium text-gray-900"> + Active {type === 'problems' ? 'Problems' : 'Modules'} + </h3> + <div className="mt-4 text-gray-500"> + {items.map((item, idx) => ( + <p className={idx === 0 ? '' : 'mt-2'}> + <Link + to={item.url} + className="font-medium text-blue-600 hover:text-blue-500 transition ease-in-out duration-150" + > + {item.label} + <span + className={ + 'ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium leading-4 ' + + statusClasses[item.status] + } + > + {item.status} + </span> + </Link> + </p> + ))} + {/*<p>*/} + {/* <a*/} + {/* href="#"*/} + {/* className="inline-flex items-center font-medium text-blue-600 hover:text-blue-500 transition ease-in-out duration-150"*/} + {/* >*/} + {/* Longest Common Subsequence*/} + {/* <span className="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium leading-4 bg-gray-100 text-gray-800">*/} + {/* Skipped*/} + {/* </span>*/} + {/* </a>*/} + {/*</p>*/} + </div> + </div> + </div> + ); +} diff --git a/src/components/Dashboard/DashboardProgress.tsx b/src/components/Dashboard/DashboardProgress.tsx new file mode 100644 index 0000000..05cf6e4 --- /dev/null +++ b/src/components/Dashboard/DashboardProgress.tsx @@ -0,0 +1,85 @@ +import * as React from 'react'; + +const ProgressBar = ({ text, green, yellow, blue }) => { + return ( + <div className="relative"> + <div className="overflow-hidden h-4 text-xs flex bg-gray-200"> + <div + style={{ width: `${green}%` }} + className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-500" + /> + <div + style={{ width: `${yellow}%` }} + className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-yellow-300" + /> + <div + style={{ width: `${blue}%` }} + className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-blue-500" + /> + </div> + <div className="text-right"> + <span className="text-sm font-semibold inline-block text-gray-800"> + {text} + </span> + </div> + </div> + ); +}; + +const FancyNumber = ({ number, text, textColor, bgColor }) => ( + <div className="text-center"> + <span + className={`text-3xl font-bold ${textColor} ${bgColor} rounded-full h-16 w-16 inline-block inline-flex items-center justify-center`} + > + {number} + </span> + <span className={`block mt-1 text-sm font-medium uppercase ${textColor}`}> + {text} + </span> + </div> +); + +export default function DashboardProgress({ + completed, + inProgress, + skipped, + notStarted, + total, +}) { + return ( + <div> + <div className="grid grid-cols-4 gap-2 mb-4"> + <FancyNumber + number={completed} + text="Completed" + textColor="text-green-800" + bgColor="bg-green-100" + /> + <FancyNumber + number={inProgress} + text="In Progress" + textColor="text-yellow-800" + bgColor="bg-yellow-100" + /> + <FancyNumber + number={skipped} + text="Skipped" + textColor="text-blue-800" + bgColor="bg-blue-50" + /> + <FancyNumber + number={notStarted} + text="Not Started" + textColor="text-gray-800" + bgColor="bg-gray-100" + /> + </div> + <ProgressBar + green={(completed / total) * 100} + yellow={(inProgress / total) * 100} + blue={(skipped / total) * 100} + text={`${total} total`} + /> + </div> + ); +} diff --git a/src/components/Dashboard/ModuleLink.tsx b/src/components/Dashboard/ModuleLink.tsx new file mode 100644 index 0000000..197dfb5 --- /dev/null +++ b/src/components/Dashboard/ModuleLink.tsx @@ -0,0 +1,156 @@ +import * as React from 'react'; +import styled, { css } from 'styled-components'; +import tw from 'twin.macro'; +import { ModuleLinkInfo } from '../../models/module'; +import { useContext } from 'react'; +import ModuleLayoutContext from '../../context/ModuleLayoutContext'; +import UserDataContext from '../../context/UserDataContext'; +import { Link } from 'gatsby'; + +const LinkWithProgress = styled.span` + ${tw`block relative`} + + &::after { + content: ''; + //left: calc(-3rem - 12px); // -(3rem padding plus half of width) + //top: calc(1.5rem - 12px); // half of (1.5 + 1.5padding) rem minus half of height + //height: 24px; + //width: 24px; + ${({ small }) => css` + left: calc(-1.75rem - ${ + small ? '8px' : '10px' + }); // -(3rem padding plus half of width) + // prettier-ignore + top: calc(1.5rem - ${ + small ? '8px' : '10px' + }); // half of (1.5 + 1.5padding) rem minus half of height + height: ${small ? '16px' : '20px'}; + width: ${small ? '16px' : '20px'}; + `} + + @media (min-width: 768px) { + ${({ small }) => css` + // prettier-ignore + left: calc(-3rem - ${ + small ? '8px' : '10px' + }); // -(3rem padding plus half of width) + `} + } + + position: absolute; + transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1) 0s; + } + + &::after { + border-radius: 100%; + + ${props => props.dotColorStyle} + ${({ small }) => small && tw`border-2 border-gray-200 bg-white`} + } + + &::before { + content: ''; + position: absolute; + width: 2px; + display: block; + left: calc(-1.75rem - 1px); + @media (min-width: 768px) { + left: calc(-3rem - 1px); // -(3rem padding plus half of width) + } + top: 0; + bottom: 0; + ${props => props.lineColorStyle}; + } + + &:first-of-type::before { + top: 22px; + } + + &:last-of-type::before { + bottom: calc(100% - 22px); + } +`; + +const StyledLink = styled.div` + ${tw`focus:outline-none transition ease-in-out duration-150 text-gray-800 hover:text-blue-700 text-xl leading-6 py-3`} + + &::before { + content: ''; + left: calc(-1.75rem - 11px); + @media (min-width: 768px) { + left: calc(-3rem - 11px); // -(3rem padding plus half of width) + } + top: calc(1.5rem - 11px); // half of 1.5rem minus half of height + height: 22px; + width: 22px; + position: absolute; + transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1) 0s; + } + + &::before { + transform: ${({ showDot }) => (showDot ? 'scale(1)' : 'scale(0.1)')}; + border-radius: 100%; + z-index: 1; + ${({ dotColorStyle }) => dotColorStyle} + } + + &:hover { + &::before { + transform: scale(1); + ${tw`bg-blue-600`} + } + } +`; + +const ModuleLink = ({ link }: { link: ModuleLinkInfo }) => { + const { userProgressOnModules } = useContext(UserDataContext); + const progress = userProgressOnModules[link.id] || 'Not Started'; + + let lineColorStyle = tw`bg-gray-200`; + let dotColorStyle = tw`bg-white`; + + if (progress === 'Reading') { + lineColorStyle = tw`bg-yellow-400`; + dotColorStyle = tw`bg-yellow-400`; + } else if (progress === 'Practicing') { + lineColorStyle = tw`bg-orange-400`; + dotColorStyle = tw`bg-orange-400`; + } else if (progress === 'Complete') { + lineColorStyle = tw`bg-green-400`; + dotColorStyle = tw`bg-green-400`; + } else if (progress === 'Skipped') { + lineColorStyle = tw`bg-blue-300`; + dotColorStyle = tw`bg-blue-300`; + } else if (progress === 'Ignored') { + lineColorStyle = tw`bg-gray-100`; + dotColorStyle = tw`bg-gray-100`; + } + + return ( + <LinkWithProgress + lineColorStyle={lineColorStyle} + dotColorStyle={dotColorStyle} + small={progress === 'Not Started' || progress === 'Ignored'} + > + <Link to={link.url}> + <StyledLink + dotColorStyle={dotColorStyle === tw`bg-gray-200`} + className="group" + > + <p + className={`${ + progress === 'Ignored' ? 'text-gray-400' : 'text-gray-600' + } group-hover:text-blue-800 transition duration-150 ease-in-out`} + > + {link.title} + </p> + <p className="block text-sm text-gray-400 group-hover:text-blue-700 transition duration-150 ease-in-out"> + {link.description} + </p> + </StyledLink> + </Link> + </LinkWithProgress> + ); +}; + +export default ModuleLink; diff --git a/src/components/Dashboard/SectionProgressBar.tsx b/src/components/Dashboard/SectionProgressBar.tsx new file mode 100644 index 0000000..60cc180 --- /dev/null +++ b/src/components/Dashboard/SectionProgressBar.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; + +export default function SectionProgressBar({ title }) { + return ( + <div className="relative"> + <div className="flex mb-1 items-center justify-between text-gray-900"> + <div> + <span className="font-semibold inline-block">{title}: 50%</span> + </div> + </div> + <div className="overflow-hidden h-2 mb-4 text-xs flex rounded bg-gray-200"> + <div + style={{ width: '45%' }} + className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-500" + /> + <div + style={{ width: '10%' }} + className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-yellow-300" + /> + <div + style={{ width: '5%' }} + className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-blue-500" + /> + </div> + </div> + ); +} diff --git a/src/components/Dashboard/WelcomeBackBanner.tsx b/src/components/Dashboard/WelcomeBackBanner.tsx new file mode 100644 index 0000000..4920cb8 --- /dev/null +++ b/src/components/Dashboard/WelcomeBackBanner.tsx @@ -0,0 +1,43 @@ +import { Link } from 'gatsby'; +import * as React from 'react'; + +export default function WelcomeBackBanner({ + lastViewedModuleURL, + lastViewedModuleLabel, +}) { + return ( + <div className="bg-blue-700 shadow hover:shadow-lg transition duration-150 ease-in-out lg:rounded-lg w-full"> + <Link + className="p-8 block sm:flex sm:items-center sm:justify-between" + to={lastViewedModuleURL || '/intro/using-this-guide'} + > + <div> + <h3 className="text-2xl leading-7 font-medium text-white"> + {lastViewedModuleURL + ? 'Welcome Back!' + : 'Welcome to the USACO Guide!'} + </h3> + <div className="mt-2 leading-5 text-teal-200"> + <p> + {lastViewedModuleURL + ? `Pick up where you left off. Your last viewed module was ${lastViewedModuleLabel}.` + : `Get started on the first module, "Using This Guide."`} + </p> + </div> + </div> + <div className="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center lg:mr-2"> + <span className="inline-flex rounded-md shadow-sm"> + <button + type="button" + className="inline-flex items-center px-4 lg:px-8 py-2 lg:py-3 border border-transparent lg:text-lg font-medium rounded-md text-white bg-blue-800 hover:bg-blue-600 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150" + > + {lastViewedModuleURL + ? `Continue: ${lastViewedModuleLabel}` + : `Get Started: Using This Guide!`} + </button> + </span> + </div> + </Link> + </div> + ); +} diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx new file mode 100644 index 0000000..476884f --- /dev/null +++ b/src/components/Logo.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { graphql, useStaticQuery } from 'gatsby'; +import Img from 'gatsby-image'; + +export default function Logo(props) { + const data = useStaticQuery(graphql` + query { + file(relativePath: { eq: "logo.png" }) { + childImageSharp { + fixed(height: 36, quality: 100) { + ...GatsbyImageSharpFixed + } + } + } + } + `); + return ( + <Img fixed={data.file.childImageSharp.fixed} alt="USACO Guide" {...props} /> + ); +} diff --git a/src/components/ModuleLayout/MarkCompleteButton.tsx b/src/components/ModuleLayout/MarkCompleteButton.tsx new file mode 100644 index 0000000..70a35d9 --- /dev/null +++ b/src/components/ModuleLayout/MarkCompleteButton.tsx @@ -0,0 +1,100 @@ +import * as React from 'react'; +import Transition from '../Transition'; +import { ModuleProgressOptions } from '../../models/module'; + +const MarkCompleteButton = ({ + state, + onChange, + dropdownAbove, +}: { + state: string; + onChange: Function; + dropdownAbove?: boolean; +}) => { + const [show, setShow] = React.useState(false); + const handleSelect = option => { + setShow(false); + onChange(option); + }; + const ref = React.useRef(); + + React.useEffect(() => { + const handleClick = e => { + // @ts-ignore + if (ref.current.contains(e.target)) return; + setShow(false); + }; + document.addEventListener('mousedown', handleClick); + return () => document.removeEventListener('mousedown', handleClick); + }, []); + + return ( + <div className="relative inline-block text-left" ref={ref}> + <div> + <span className="rounded-md shadow-sm"> + <button + type="button" + className="inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800 transition ease-in-out duration-150" + id="options-menu" + aria-haspopup="true" + aria-expanded="true" + onClick={() => setShow(!show)} + > + {state} + <svg + className="-mr-1 ml-2 h-5 w-5" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" + clipRule="evenodd" + /> + </svg> + </button> + </span> + </div> + + <Transition + show={show} + enter="transition ease-out duration-100" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leave="transition ease-in duration-75" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + > + <div + className={`${ + dropdownAbove + ? 'origin-bottom-right bottom-0 mb-12' + : 'origin-top-right' + } right-0 absolute z-10 mt-2 w-32 rounded-md shadow-lg`} + > + <div className="rounded-md bg-white shadow-xs"> + <div + className="py-1" + role="menu" + aria-orientation="vertical" + aria-labelledby="options-menu" + > + {ModuleProgressOptions.map(option => ( + <button + key={option} + onClick={() => handleSelect(option)} + className="block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900" + role="menuitem" + > + {option} + </button> + ))} + </div> + </div> + </div> + </Transition> + </div> + ); +}; + +export default MarkCompleteButton; diff --git a/src/components/ModuleLayout/ModuleConfetti.tsx b/src/components/ModuleLayout/ModuleConfetti.tsx new file mode 100644 index 0000000..4451cda --- /dev/null +++ b/src/components/ModuleLayout/ModuleConfetti.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; +import Confetti from '../Confetti'; +import useWindowDimensions from '../../hooks/useWindowDimensions'; + +const ModuleConfetti = ({ show, onDone }) => { + let config = { + numParticles: 100, + gravity: 250, + speed: 250, + scale: 0.5, + spin: 0, + twist: 0, + }; + const { numParticles, gravity, speed, scale, spin, twist } = config; + const { height, width } = useWindowDimensions(); + + React.useEffect(() => { + let timeout = null; + if (show) timeout = setTimeout(() => onDone(), 3000); + if (timeout) return () => clearTimeout(timeout); + }, [show]); + + if (!show) return null; + return ( + <div className="fixed inset-0 pointer-events-none z-20"> + <Confetti + width={width} + height={height} + makeItRainOn="mount" + emitDuration={1000} + numParticles={numParticles} + gravity={gravity} + minSpeed={speed} + maxSpeed={speed * 3} + minScale={scale} + maxScale={scale * 2} + spin={spin} + twist={twist} + /> + </div> + ); +}; + +export default ModuleConfetti; diff --git a/src/components/ModuleLayout/ModuleFrequencyDots.tsx b/src/components/ModuleLayout/ModuleFrequencyDots.tsx new file mode 100644 index 0000000..002f30c --- /dev/null +++ b/src/components/ModuleLayout/ModuleFrequencyDots.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; + +export default function ModuleFrequencyDots({ count, color, totalCount }) { + const emptyCircle = 'text-gray-300'; + return ( + <> + {new Array(totalCount).fill(null).map((_, idx) => ( + <svg + className={`-ml-1 mr-1.5 h-2.5 w-2.5 ${ + idx >= count ? emptyCircle : color + }`} + fill="currentColor" + viewBox="0 0 8 8" + key={idx} + > + <circle cx="4" cy="4" r="3" /> + </svg> + ))} + </> + ); +} diff --git a/src/components/ModuleLayout/ModuleLayout.tsx b/src/components/ModuleLayout/ModuleLayout.tsx new file mode 100644 index 0000000..28ffb6b --- /dev/null +++ b/src/components/ModuleLayout/ModuleLayout.tsx @@ -0,0 +1,560 @@ +import * as React from 'react'; +import Transition from '../Transition'; +import { useContext, useRef, useState } from 'react'; +import { + ModuleFrequency, + ModuleInfo, + ModuleLinkInfo, +} from '../../models/module'; +import { graphql, Link, useStaticQuery } from 'gatsby'; +import MODULE_ORDERING, { SECTION_LABELS } from '../../../content/ordering'; +import ModuleFrequencyDots from './ModuleFrequencyDots'; +import ContactUsSlideover from '../ContactUsSlideover/ContactUsSlideover'; +import MarkCompleteButton from './MarkCompleteButton'; +import ModuleConfetti from './ModuleConfetti'; +import TextTooltip from '../Tooltip/TextTooltip'; +import UserDataContext, { UserLang } from '../../context/UserDataContext'; +import { SidebarNav } from './SidebarNav/SidebarNav'; +import { graphqlToModuleLinks } from '../../utils/utils'; +import ModuleLayoutContext from '../../context/ModuleLayoutContext'; +import TableOfContentsSidebar from './TableOfContents/TableOfContentsSidebar'; +import TableOfContentsBlock from './TableOfContents/TableOfContentsBlock'; +import Logo from '../Logo'; + +const Frequency = ({ frequency }: { frequency: ModuleFrequency }) => { + const textColors = [ + 'text-red-600', + 'text-orange-600', + 'text-yellow-600', + 'text-teal-600', + 'text-green-600', + ]; + const circleColors = [ + 'text-red-500', + 'text-orange-500', + 'text-yellow-500', + 'text-teal-500', + 'text-green-500', + ]; + const labels = [ + 'Has Not Appeared', + 'Rare', + 'Not Frequent', + 'Somewhat Frequent', + 'Very Frequent', + ]; + const hints = [ + 'Historically, this module has not appeared in this division before. However, it can still show up in future contests.', + 'Historically, this module has only appeared in this division once or twice.', + 'Historically, this module infrequently appears in this division.', + 'Historically, this module has appeared somewhat frequently for this division.', + 'Historically, this module has been known to appear at least once per contest. However, this does not guarantee that it will show up again in future contests.', + ]; + + return ( + <span + className={`inline-flex items-center font-medium ${textColors[frequency]}`} + > + <ModuleFrequencyDots + count={frequency} + totalCount={4} + color={circleColors[frequency]} + /> + <TextTooltip position="bottom" content={hints[frequency]}> + {labels[frequency]} + </TextTooltip> + </span> + ); +}; + +const Breadcrumbs = () => { + const moduleLayoutInfo = useContext(ModuleLayoutContext); + const module = moduleLayoutInfo.module; + return ( + <nav className="flex flex-wrap items-center text-sm leading-loose font-medium"> + <Link + to="/dashboard/" + className="text-gray-500 hover:text-gray-700 transition duration-150 ease-in-out" + > + Home + </Link> + <svg + className="flex-shrink-0 mx-2 h-5 w-5 text-gray-400" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" + clipRule="evenodd" + /> + </svg> + <Link + to={`/${module.section}/`} + className="text-gray-500 hover:text-gray-700 transition duration-150 ease-in-out" + > + {SECTION_LABELS[module.section]} + </Link> + <svg + className="flex-shrink-0 mx-2 h-5 w-5 text-gray-400" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" + clipRule="evenodd" + /> + </svg> + <span className="text-gray-500 whitespace-no-wrap">{module.title}</span> + </nav> + ); +}; + +const SidebarBottomButtons = ({ onContactUs }) => { + const languages = { + showAll: 'All', + cpp: 'C++', + java: 'Java', + py: 'Python', + }; + const nextLang: { [key: string]: UserLang } = { + showAll: 'cpp', + cpp: 'java', + java: 'py', + py: 'cpp', + }; + const userSettings = useContext(UserDataContext); + return ( + <> + <div className="flex-shrink-0 border-t border-gray-200 flex"> + <button + className="group flex-1 flex items-center p-4 text-sm leading-5 font-medium text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-100 transition ease-in-out duration-150" + onClick={() => userSettings.setLang(nextLang[userSettings.lang])} + > + <svg + className="mr-4 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + > + <path d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /> + </svg> + Language: {languages[userSettings.lang]} + </button> + </div> + <div className="flex-shrink-0 border-t border-gray-200 flex"> + <button + className="group flex-1 flex items-center p-4 text-sm leading-5 font-medium text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-100 transition ease-in-out duration-150" + onClick={onContactUs} + > + <svg + className="mr-4 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + > + <path d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" /> + </svg> + Contact Us + </button> + </div> + </> + ); +}; + +const NavBar = ({ alignNavButtonsRight = true }) => { + const moduleLayoutInfo = useContext(ModuleLayoutContext); + const { module, moduleLinks } = moduleLayoutInfo; + const sortedModuleLinks = React.useMemo(() => { + let links: ModuleLinkInfo[] = []; + for (let group of MODULE_ORDERING[module.section]) { + for (let id of group.items) { + links.push(moduleLinks.find(x => x.id === id)); + } + } + return links; + }, [moduleLinks]); + let moduleIdx = React.useMemo( + () => sortedModuleLinks.findIndex(x => x.id === module.id), + [module, sortedModuleLinks] + ); + let prevModule = moduleIdx === 0 ? null : sortedModuleLinks[moduleIdx - 1]; + let nextModule = + moduleIdx === sortedModuleLinks.length - 1 + ? null + : sortedModuleLinks[moduleIdx + 1]; + + const disabledClasses = 'text-gray-200 pointer-events-none'; + const activeClasses = + 'text-gray-500 hover:text-gray-800 transition duration-150 ease-in-out'; + return ( + <div + className={`flex ${ + alignNavButtonsRight ? 'sm:justify-between' : 'justify-between' + }`} + > + {alignNavButtonsRight && <div className="flex-1 sm:hidden" />} + <span className="-ml-4 rounded-md"> + <Link + to={prevModule === null ? module.url : prevModule.url} + className={ + 'inline-flex items-center px-4 py-2 text-sm leading-5 font-medium rounded-md ' + + (prevModule === null ? disabledClasses : activeClasses) + } + > + <svg + className="-ml-0.5 mr-1 h-4 w-4" + fill="none" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path d="M15 19l-7-7 7-7" /> + </svg> + Prev + </Link> + </span> + <div className="hidden sm:flex items-center"> + <Breadcrumbs /> + </div> + <span className="rounded-md -mr-4"> + <Link + to={nextModule === null ? module.url : nextModule.url} + className={ + 'inline-flex items-center px-4 py-2 text-sm leading-5 font-medium rounded-md ' + + (nextModule === null ? disabledClasses : activeClasses) + } + > + Next + <svg + className="-mr-0.5 ml-1 h-4 w-4" + fill="none" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path d="M9 5l7 7-7 7" /> + </svg> + </Link> + </span> + </div> + ); +}; + +const renderPrerequisite = (prerequisite, moduleLinks: ModuleLinkInfo[]) => { + let moduleLink = moduleLinks.find(x => x.id === prerequisite); + if (moduleLink) + return ( + <li key={prerequisite}> + <Link to={moduleLink.url} className="underline text-black"> + {SECTION_LABELS[moduleLink.section]} - {moduleLink.title} + </Link> + </li> + ); + return <li key={prerequisite}>{prerequisite}</li>; +}; + +export default function ModuleLayout({ + module, + children, +}: { + module: ModuleInfo; + children: React.ReactNode; +}) { + const { userProgressOnModules, setModuleProgress, lang } = useContext( + UserDataContext + ); + const [isMobileNavOpen, setIsMobileNavOpen] = useState(false); + const [isContactUsActive, setIsContactUsActive] = useState(false); + const [isConfettiActive, setIsConfettiActive] = useState(false); + const moduleProgress = + (userProgressOnModules && userProgressOnModules[module.id]) || + 'Not Started'; + + const tableOfContents = + lang in module.toc ? module.toc[lang] : module.toc['cpp']; + + const data = useStaticQuery(graphql` + query { + allMdx { + edges { + node { + frontmatter { + title + id + } + fields { + division + } + } + } + } + } + `); + const moduleLinks = React.useMemo(() => graphqlToModuleLinks(data.allMdx), [ + data.allMdx, + ]); + + const handleCompletionChange = progress => { + if (moduleProgress === progress) return; + setModuleProgress(module.id, progress); + if ( + moduleProgress !== 'Complete' && + (progress === 'Practicing' || progress === 'Complete') + ) + setIsConfettiActive(true); + }; + + return ( + <ModuleLayoutContext.Provider value={{ module, moduleLinks }}> + <ModuleConfetti + show={isConfettiActive} + onDone={() => setIsConfettiActive(false)} + /> + <Transition show={isMobileNavOpen} timeout={300}> + <div className="lg:hidden"> + <div className="fixed inset-0 flex z-40"> + <Transition + enter="transition-opacity ease-linear duration-300" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="transition-opacity ease-linear duration-300" + leaveFrom="opacity-100" + leaveTo="opacity-0" + > + <div + className="fixed inset-0" + onClick={() => setIsMobileNavOpen(false)} + > + <div className="absolute inset-0 bg-gray-600 opacity-75" /> + </div> + </Transition> + + <Transition + enter="transition ease-in-out duration-300 transform" + enterFrom="-translate-x-full" + enterTo="translate-x-0" + leave="transition ease-in-out duration-300 transform" + leaveFrom="translate-x-0" + leaveTo="-translate-x-full" + > + <div className="relative flex-1 flex flex-col max-w-xs w-full bg-white"> + <div className="absolute top-0 right-0 -mr-14 p-1"> + <button + className="flex items-center justify-center h-12 w-12 rounded-full focus:outline-none focus:bg-gray-600" + aria-label="Close sidebar" + onClick={() => setIsMobileNavOpen(false)} + > + <svg + className="h-6 w-6 text-white" + stroke="currentColor" + fill="none" + viewBox="0 0 24 24" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={2} + d="M6 18L18 6M6 6l12 12" + /> + </svg> + </button> + </div> + <div className="flex-1 h-0 pt-5 overflow-y-auto"> + <Link + className="flex-shrink-0 flex items-center px-4" + to="/dashboard/" + > + <Logo /> + </Link> + <div className="mt-4 px-6"> + <Breadcrumbs /> + </div> + <nav className="mt-6"> + <SidebarNav /> + </nav> + </div> + <SidebarBottomButtons + onContactUs={() => { + setIsMobileNavOpen(false); + setIsContactUsActive(true); + }} + /> + </div> + </Transition> + <div className="flex-shrink-0 w-14"> + {/* Force sidebar to shrink to fit close icon */} + </div> + </div> + </div> + </Transition> + {/* Static sidebar for desktop */} + <div className="hidden lg:flex lg:flex-shrink-0"> + <div + className="flex flex-col border-r border-gray-200 bg-white" + style={{ width: '20rem' }} + > + <div className="h-0 flex-1 flex flex-col pt-5 overflow-y-auto"> + <Link + className="flex items-center flex-shrink-0 px-4 pb-2" + to="/dashboard/" + > + <Logo /> + </Link> + {/* Sidebar component, swap this element with another sidebar if you like */} + <nav className="mt-2 flex-1 bg-white"> + <SidebarNav /> + </nav> + </div> + <SidebarBottomButtons + onContactUs={() => setIsContactUsActive(true)} + /> + </div> + </div> + <div className="flex flex-col w-0 flex-1 overflow-hidden"> + <div className="lg:hidden pl-1 pt-1 sm:pl-3 sm:pt-3 flex items-center"> + <button + className="flex-shrink-0 -ml-0.5 -mt-0.5 h-12 w-12 inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900 focus:outline-none focus:bg-gray-200 transition ease-in-out duration-150" + aria-label="Open sidebar" + onClick={() => setIsMobileNavOpen(true)} + > + <svg + className="h-6 w-6" + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={2} + d="M4 6h16M4 12h16M4 18h16" + /> + </svg> + </button> + <div className="flex-1 ml-4 mr-4 sm:mr-6"> + <NavBar /> + </div> + </div> + <main + className="flex-1 relative z-0 overflow-y-auto sm:pt-2 pb-6 focus:outline-none" + tabIndex={0} + > + <div className="mx-auto"> + <div className="flex justify-center"> + <div className="flex-1 max-w-4xl px-4 sm:px-6 lg:px-8 w-0"> + <div className="hidden lg:block"> + <NavBar /> + </div> + + <div className="px-1.5 lg:mt-8"> + {module.frequency !== null && ( + <Frequency frequency={module.frequency} /> + )} + </div> + <div className="sm:flex sm:items-center sm:justify-between mb-4"> + <div className="flex-1 min-w-0"> + <h1 className="text-2xl font-bold text-gray-900 sm:text-3xl"> + {module.title} + </h1> + <p className={`text-gray-500`}> + Author{module.author.indexOf(',') !== -1 ? 's' : ''}:{' '} + {module.author} + </p> + </div> + <div className="hidden lg:flex-shrink-0 lg:flex ml-4"> + <MarkCompleteButton + state={moduleProgress} + onChange={handleCompletionChange} + /> + </div> + </div> + + {module.prerequisites && ( + <div className="rounded-md bg-blue-50 p-4 mb-4"> + <div className="flex"> + <div className="flex-shrink-0"> + <svg + className="h-5 w-5 text-blue-400" + fill="currentColor" + viewBox="0 0 20 20" + > + <path + fillRule="evenodd" + d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="ml-3"> + <h3 className="text-sm leading-5 font-medium text-blue-800"> + Prerequisites + </h3> + <div className="mt-2 text-sm leading-5 text-blue-800"> + <ul className="list-disc list-inside pl-3 space-y-1"> + {module.prerequisites.map(x => + renderPrerequisite(x, moduleLinks) + )} + </ul> + </div> + </div> + </div> + </div> + )} + + {module.description && ( + <p className="font-bold mb-4 bg-green-50 border-l-4 border-green-400 text-green-800 p-4"> + {module.description} + </p> + )} + + <div className="xl:hidden"> + <TableOfContentsBlock tableOfContents={tableOfContents} /> + </div> + + {children} + + <h3 className="text-lg leading-6 font-medium text-gray-900 text-center mb-8 border-t border-gray-200 pt-8"> + <TextTooltip content="You can use this as a way to track your progress throughout this guide."> + Module Progress + </TextTooltip> + : + <span className="ml-4"> + <MarkCompleteButton + onChange={handleCompletionChange} + state={moduleProgress} + dropdownAbove + /> + </span> + </h3> + + <div className="border-t border-gray-200 pt-4"> + <NavBar alignNavButtonsRight={false} /> + </div> + </div> + <div className="hidden xl:block ml-6 w-64 mt-48 flex-shrink-0"> + <TableOfContentsSidebar tableOfContents={tableOfContents} /> + </div> + </div> + </div> + </main> + </div> + <ContactUsSlideover + isOpen={isContactUsActive} + onClose={() => setIsContactUsActive(false)} + activeModule={module} + /> + </ModuleLayoutContext.Provider> + ); +} diff --git a/src/components/ModuleLayout/SidebarNav/Accordion.tsx b/src/components/ModuleLayout/SidebarNav/Accordion.tsx new file mode 100644 index 0000000..9cac7b6 --- /dev/null +++ b/src/components/ModuleLayout/SidebarNav/Accordion.tsx @@ -0,0 +1,61 @@ +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import styled from 'styled-components'; +import tw from 'twin.macro'; +import useStickyState from '../../../hooks/useStickyState'; + +const Container = styled.div` + &:last-of-type { + ${tw`border-b`} + } +`; + +const Label = styled.div` + &:hover { + //background-color: #f7faff + ${tw`bg-blue-50`} + } +`; + +export default function Accordion({ label, isActive, children }) { + const [expanded, setExpanded] = useStickyState( + true, + 'guide:sidebarnav:isexpanded:' + label + ); + useEffect(() => { + if (isActive) setExpanded(true); + }, [isActive]); + return ( + <Container + className="border-t border-gray-200" + style={isActive ? { backgroundColor: '#f7faff' } : null} + > + <Label + className="font-semibold cursor-pointer relative flex items-center px-4 py-3 text-sm leading-5 transition ease-in-out duration-150 " + onClick={() => setExpanded(!expanded)} + > + <span className="flex-1">{label}</span> + <svg + className="h-5 w-5 flex-shrink-0 text-gray-600" + fill="currentColor" + viewBox="0 0 20 20" + > + {expanded ? ( + <path + fillRule="evenodd" + d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" + clipRule="evenodd" + /> + ) : ( + <path + fillRule="evenodd" + d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" + clipRule="evenodd" + /> + )} + </svg> + </Label> + {expanded && children} + </Container> + ); +} diff --git a/src/components/ModuleLayout/SidebarNav/ItemLink.tsx b/src/components/ModuleLayout/SidebarNav/ItemLink.tsx new file mode 100644 index 0000000..8def5dc --- /dev/null +++ b/src/components/ModuleLayout/SidebarNav/ItemLink.tsx @@ -0,0 +1,149 @@ +import * as React from 'react'; +import { Link } from 'gatsby'; +import { ModuleLinkInfo } from '../../../models/module'; +import styled from 'styled-components'; +import tw from 'twin.macro'; +import { useContext } from 'react'; +import ModuleLayoutContext from '../../../context/ModuleLayoutContext'; +import UserDataContext from '../../../context/UserDataContext'; + +const LinkWithProgress = styled.span` + ${tw`block relative`} + + &::after { + content: ''; + left: 24px; + top: 18px; + height: 8px; + width: 8px; + position: absolute; + transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1) 0s; + } + + &::after { + border-radius: 100%; + ${props => props.dotColorStyle}; + } + + &::before { + content: ''; + position: absolute; + width: 2px; + display: block; + left: 27px; + top: 0; + bottom: 0; + ${props => props.lineColorStyle}; + } + + &:first-of-type::before { + top: 22px; + } + + &:last-of-type::before { + bottom: calc(100% - 22px); + } +`; + +const StyledLink = styled.span` + ${tw`focus:outline-none transition ease-in-out duration-150 hover:text-blue-700 hover:bg-blue-50 focus:bg-blue-100 flex items-center pl-12 pr-4 py-3 text-sm leading-5`} + + ${({ textStyle }) => textStyle} + + + &::before { + content: ''; + left: 24px; + top: 18px; + height: 8px; + width: 8px; + position: absolute; + transition: all 250ms cubic-bezier(0.4, 0, 0.2, 1) 0s; + } + + &::before { + transform: ${({ isActive }) => (isActive ? 'scale(1)' : 'scale(0.1)')}; + border-radius: 100%; + z-index: 1; + ${({ dotColorStyle }) => dotColorStyle} + } + + &:hover { + &::before { + transform: scale(1); + ${tw`bg-blue-600`} + } + } +`; + +const ItemLink = ({ link }: { link: ModuleLinkInfo }) => { + const { module } = useContext(ModuleLayoutContext); + const isActive = module.id === link.id; + const itemRef = React.useRef(null); + + React.useEffect(() => { + if (isActive) { + itemRef.current.scrollIntoView({ block: `center` }); + } + }, [isActive]); + + const { userProgressOnModules } = useContext(UserDataContext); + const progress = userProgressOnModules[link.id] || 'Not Started'; + + let lineColorStyle = tw`bg-gray-200`; + let dotColorStyle = tw`bg-gray-200`; + let activeTextStyle = tw`text-blue-700 font-medium`; + + if (isActive) { + lineColorStyle = tw`bg-blue-700`; + dotColorStyle = tw`bg-blue-700`; + } + + if (progress === 'Reading') { + lineColorStyle = tw`bg-yellow-400`; + dotColorStyle = tw`bg-yellow-400`; + activeTextStyle = tw`text-yellow-700 font-medium`; + } else if (progress === 'Practicing') { + lineColorStyle = tw`bg-orange-400`; + dotColorStyle = tw`bg-orange-400`; + activeTextStyle = tw`text-orange-700 font-medium`; + } else if (progress === 'Complete') { + lineColorStyle = tw`bg-green-400`; + dotColorStyle = tw`bg-green-400`; + activeTextStyle = tw`text-green-700 font-medium`; + } else if (progress === 'Skipped') { + lineColorStyle = tw`bg-blue-300`; + dotColorStyle = tw`bg-blue-300`; + activeTextStyle = tw`text-blue-700 font-medium`; + } else if (progress === 'Ignored') { + lineColorStyle = tw`bg-gray-100`; + dotColorStyle = tw`bg-gray-100`; + activeTextStyle = tw`text-gray-400 font-medium`; + } + + return ( + <LinkWithProgress + lineColorStyle={lineColorStyle} + dotColorStyle={dotColorStyle} + > + <Link to={link.url}> + <StyledLink + isActive={isActive} + ref={itemRef} + dotColorStyle={dotColorStyle === tw`bg-gray-200`} + textStyle={ + isActive + ? activeTextStyle + : progress === 'Ignored' + ? tw`text-gray-400` + : tw`text-gray-600` + } + > + {link.title} + </StyledLink> + </Link> + </LinkWithProgress> + ); +}; + +export default ItemLink; diff --git a/src/components/ModuleLayout/SidebarNav/SidebarNav.tsx b/src/components/ModuleLayout/SidebarNav/SidebarNav.tsx new file mode 100644 index 0000000..83f1d97 --- /dev/null +++ b/src/components/ModuleLayout/SidebarNav/SidebarNav.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { ModuleLinkInfo } from '../../../models/module'; +import ItemLink from './ItemLink'; +import Accordion from './Accordion'; +import MODULE_ORDERING, { Chapter } from '../../../../content/ordering'; +import { useContext } from 'react'; +import ModuleLayoutContext from '../../../context/ModuleLayoutContext'; + +export interface NavLinkGroup { + label: string; + children: ModuleLinkInfo[]; +} + +export const SidebarNav = () => { + const { module, moduleLinks } = useContext(ModuleLayoutContext); + + const links: NavLinkGroup[] = React.useMemo(() => { + return MODULE_ORDERING[module.section].map((category: Chapter) => ({ + label: category.name, + children: category.items.map( + moduleID => moduleLinks.find(link => link.id === moduleID) // lol O(n^2)? + ), + })); + }, [module.section, moduleLinks]); + + return ( + <> + {links.map(group => ( + <Accordion + key={group.label} + label={group.label} + isActive={group.children.findIndex(x => x.id === module.id) !== -1} + > + {group.children.map(link => ( + <ItemLink key={link.id} link={link} /> + ))} + </Accordion> + ))} + </> + ); +}; diff --git a/src/components/ModuleLayout/TableOfContents/TableOfContentsBlock.tsx b/src/components/ModuleLayout/TableOfContents/TableOfContentsBlock.tsx new file mode 100644 index 0000000..46a87ff --- /dev/null +++ b/src/components/ModuleLayout/TableOfContents/TableOfContentsBlock.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { TOCHeading } from '../../../models/module'; +import genLinksFromTOCHeadings from './genLinksFromTOCHeadings'; + +const TableOfContentsBlock = ({ + tableOfContents, +}: { + tableOfContents: TOCHeading[]; +}) => { + let links = genLinksFromTOCHeadings( + tableOfContents, + _ => + 'block mb-2 transition duration-150 ease-in-out text-gray-600 hover:underline hover:text-blue-600' + ); + return ( + <div> + <h2 className="uppercase text-gray-500 font-bold mt-8 mb-3 tracking-wider"> + Table of Contents + </h2> + {links} + <hr className="mt-6 mb-2" /> + </div> + ); +}; + +export default TableOfContentsBlock; diff --git a/src/components/ModuleLayout/TableOfContents/TableOfContentsSidebar.tsx b/src/components/ModuleLayout/TableOfContents/TableOfContentsSidebar.tsx new file mode 100644 index 0000000..37a5a7e --- /dev/null +++ b/src/components/ModuleLayout/TableOfContents/TableOfContentsSidebar.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { TOCHeading } from '../../../models/module'; +import { Link } from 'gatsby'; +import { useActiveHash } from '../../../hooks/useActiveHash'; +import { useMemo } from 'react'; +import genLinksFromTOCHeadings from './genLinksFromTOCHeadings'; + +const TableOfContentsSidebar = ({ + tableOfContents, +}: { + tableOfContents: TOCHeading[]; +}) => { + const hashes = useMemo(() => tableOfContents.map(heading => heading.slug), [ + tableOfContents, + ]); + const activeHash = useActiveHash(hashes, '10px 0px 0px 0px'); + + let links = genLinksFromTOCHeadings( + tableOfContents, + heading => + 'block mb-1 text-sm transition duration-150 ease-in-out ' + + (activeHash === heading.slug + ? 'underline text-blue-600' + : 'text-gray-600 hover:underline hover:text-blue-600') + ); + + return ( + <div className="sticky" style={{ top: '2.5rem' }}> + <h2 className="uppercase text-gray-500 font-bold mb-4 text-sm tracking-wider"> + Table of Contents + </h2> + {links} + </div> + ); +}; + +export default TableOfContentsSidebar; diff --git a/src/components/ModuleLayout/TableOfContents/genLinksFromTOCHeadings.tsx b/src/components/ModuleLayout/TableOfContents/genLinksFromTOCHeadings.tsx new file mode 100644 index 0000000..1e37b35 --- /dev/null +++ b/src/components/ModuleLayout/TableOfContents/genLinksFromTOCHeadings.tsx @@ -0,0 +1,44 @@ +import { TOCHeading } from '../../../models/module'; +import * as React from 'react'; +import { Link } from 'gatsby'; + +export default function genLinksFromTOCHeadings( + headings: TOCHeading[], + getClasses: (heading: TOCHeading) => string +) { + let indentationLevels = ['0', '1.5rem', '3rem', '4.5rem']; + let links: React.ReactNode[] = []; + let curDepth = -1; + let indentIdx = 0; + headings.forEach((heading, idx) => { + if (curDepth === -1) curDepth = heading.depth; + if (heading.depth > curDepth) { + indentIdx++; + } else if (heading.depth < curDepth) { + indentIdx = Math.max(0, indentIdx - (curDepth - heading.depth)); + } + curDepth = heading.depth; + links.push( + <Link + key={heading.slug} + to={'#' + heading.slug} + className={getClasses(heading)} + style={{ + marginLeft: indentationLevels[indentIdx], + marginTop: + indentIdx === 0 && + ((idx !== 0 && headings[idx - 1].depth > heading.depth) || + (idx !== headings.length - 1 && + headings[idx + 1].depth > heading.depth)) + ? '1rem' + : indentIdx === 0 + ? '0.5rem' + : 0, + }} + > + {heading.value} + </Link> + ); + }); + return links; +} diff --git a/src/components/SyllabusModule.tsx b/src/components/SyllabusModule.tsx index 103880b..e1b93ad 100644 --- a/src/components/SyllabusModule.tsx +++ b/src/components/SyllabusModule.tsx @@ -83,7 +83,7 @@ const SyllabusModule = ({ </div> )} <div className="px-4 py-4 sm:p-6"> - {children} + <p>{children}</p> {author && <p className="mt-2">Author: {author}</p>} </div> diff --git a/src/components/Tooltip/Asterisk.tsx b/src/components/Tooltip/Asterisk.tsx new file mode 100644 index 0000000..ddae5f4 --- /dev/null +++ b/src/components/Tooltip/Asterisk.tsx @@ -0,0 +1,37 @@ +// Heavily inspired by https://joshwcomeau.com/ + +import * as React from 'react'; +import 'tippy.js/animations/scale-subtle.css'; +import 'tippy.js/dist/tippy.css'; +import 'tippy.js/themes/material.css'; + +import Tooltip from './Tooltip'; + +const Asterisk = ({ children, position = 'top' }) => { + return ( + <span className="inline-block h-4 relative" style={{ width: 9 }}> + <Tooltip content={children} position={position} type="asterisk"> + <button + className="absolute block cursor-pointer focus:outline-none" + style={{ + transform: 'translateY(5px)', + top: '-19px', + left: '-10px', + right: '-10px', + bottom: '-10px', + padding: '10px', + }} + > + <span + className="relative text-lg font-bold text-blue-600" + style={{ bottom: '1px' }} + > + * + </span> + </button> + </Tooltip> + </span> + ); +}; + +export default Asterisk; diff --git a/src/components/Tooltip/TextTooltip.tsx b/src/components/Tooltip/TextTooltip.tsx new file mode 100644 index 0000000..dbb2877 --- /dev/null +++ b/src/components/Tooltip/TextTooltip.tsx @@ -0,0 +1,17 @@ +// Heavily inspired by https://joshwcomeau.com/ + +import * as React from 'react'; +import Tooltip from './Tooltip'; + +const TextTooltip = ({ children, ...props }) => { + return ( + // @ts-ignore + <Tooltip {...props}> + <span className="border-b border-dashed border-gray-600 cursor-pointer"> + {children} + </span> + </Tooltip> + ); +}; + +export default TextTooltip; diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx new file mode 100644 index 0000000..0f3b6b6 --- /dev/null +++ b/src/components/Tooltip/Tooltip.tsx @@ -0,0 +1,66 @@ +// Heavily inspired by https://joshwcomeau.com/ + +import * as React from 'react'; +import Tippy from '@tippyjs/react'; +import 'tippy.js/animations/scale-subtle.css'; +import 'tippy.js/dist/tippy.css'; +import 'tippy.js/themes/material.css'; +import styled from 'styled-components'; + +const StyledTippy = styled(Tippy)` + background-color: #252f3f !important; + padding: 0.5rem; + line-height: 1.5; + font-size: 1rem !important; + font-weight: normal !important; + text-align: center; + + &[data-placement^='top'] > .tippy-arrow::before { + border-top-color: #252f3f !important; + } + &[data-placement^='bottom'] > .tippy-arrow::before { + border-bottom-color: #252f3f !important; + } + &[data-placement^='left'] > .tippy-arrow::before { + border-left-color: #252f3f !important; + } + &[data-placement^='right'] > .tippy-arrow::before { + border-right-color: #252f3f !important; + } +`; + +const AsteriskTippy = styled(StyledTippy)` + transform: ${p => + p.placement === 'top' ? 'translateY(10px)' : 'translateY(-7px)'}; +`; + +const CompactTippy = styled(StyledTippy)` + font-size: 0.875rem !important; + padding: 0rem; +`; + +const Tooltip = ({ + children, + content, + position = 'top', + type = 'normal', + ...other +}) => { + let Component = StyledTippy; + if (type === 'asterisk') Component = AsteriskTippy; + else if (type === 'compact') Component = CompactTippy; + return ( + <Component + content={content} + animation="scale-subtle" + theme="material" + duration={200} + placement={position} + {...other} + > + {children} + </Component> + ); +}; + +export default Tooltip; diff --git a/src/components/TopNavigationBar.tsx b/src/components/TopNavigationBar.tsx new file mode 100644 index 0000000..f027ba3 --- /dev/null +++ b/src/components/TopNavigationBar.tsx @@ -0,0 +1,147 @@ +import * as React from 'react'; +import { useState } from 'react'; +import { SECTION_LABELS, SECTIONS } from '../../content/ordering'; +import { Link } from 'gatsby'; +import Logo from './Logo'; + +export default function TopNavigationBar() { + const [isMobileNavOpen, setIsMobileNavOpen] = useState(false); + const links = [ + { + label: 'Dashboard', + url: '/dashboard/', + }, + ...SECTIONS.map(section => ({ + label: SECTION_LABELS[section], + url: `/${section}/`, + })), + ]; + return ( + <nav className="bg-white shadow relative"> + <div className="max-w-7xl mx-auto px-2 sm:px-4 lg:px-8"> + <div className="flex justify-between h-16"> + <div className="flex px-2 lg:px-0"> + <Link to="/" className="flex-shrink-0 flex items-center"> + {/*<img*/} + {/* className="block sm:hidden h-8 w-auto"*/} + {/* src={logoSquare}*/} + {/* alt="USACO Guide"*/} + {/*/>*/} + {/*<img*/} + {/* className="hidden sm:block h-12 w-auto"*/} + {/* src={logo}*/} + {/* alt="USACO Guide"*/} + {/*/>*/} + <Logo className="block" /> + </Link> + <div className="hidden lg:ml-6 xl:ml-12 lg:flex space-x-8"> + {links.map((link, idx) => ( + <Link + key={link.url} + to={link.url} + getProps={({ isCurrent }) => ({ + className: isCurrent + ? 'inline-flex items-center px-1 pt-1 border-b-2 border-blue-500 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-blue-700 transition duration-150 ease-in-out' + : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out', + })} + > + {link.label} + </Link> + ))} + </div> + </div> + <div className="flex-1 flex items-center justify-center px-2 md:px-0 lg:ml-6 lg:justify-end"> + {/*<div className="max-w-lg w-full lg:max-w-xs">*/} + {/* <label htmlFor="search" className="sr-only">*/} + {/* Search*/} + {/* </label>*/} + {/* <div className="relative">*/} + {/* <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">*/} + {/* <svg*/} + {/* className="h-5 w-5 text-gray-400"*/} + {/* fill="currentColor"*/} + {/* viewBox="0 0 20 20"*/} + {/* >*/} + {/* <path*/} + {/* fillRule="evenodd"*/} + {/* d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"*/} + {/* clipRule="evenodd"*/} + {/* />*/} + {/* </svg>*/} + {/* </div>*/} + {/* <input*/} + {/* id="search"*/} + {/* className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:border-blue-300 focus:shadow-outline-blue sm:text-sm transition duration-150 ease-in-out"*/} + {/* placeholder="Search"*/} + {/* type="search"*/} + {/* />*/} + {/* </div>*/} + {/*</div>*/} + </div> + <div className="flex items-center lg:hidden"> + {/* Mobile menu button */} + <button + className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out" + aria-label="Main menu" + aria-expanded="false" + onClick={() => setIsMobileNavOpen(!isMobileNavOpen)} + > + {/* Icon when menu is closed. */} + {/* Menu open: "hidden", Menu closed: "block" */} + <svg + className={`${isMobileNavOpen ? 'hidden' : 'block'} h-6 w-6`} + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={2} + d="M4 6h16M4 12h16M4 18h16" + /> + </svg> + {/* Icon when menu is open. */} + {/* Menu open: "block", Menu closed: "hidden" */} + <svg + className={`${isMobileNavOpen ? 'block' : 'hidden'} h-6 w-6`} + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={2} + d="M6 18L18 6M6 6l12 12" + /> + </svg> + </button> + </div> + </div> + </div> + {/* + Mobile menu, toggle classes based on menu state. + + Menu open: "block", Menu closed: "hidden" + */} + <div className={`${isMobileNavOpen ? 'block' : 'hidden'} lg:hidden`}> + <div className="pt-2 pb-3 space-y-1"> + {links.map((link, idx) => ( + <Link + key={link.url} + to={link.url} + getProps={({ isCurrent }) => ({ + className: isCurrent + ? 'block pl-3 pr-4 py-2 border-l-4 border-indigo-500 text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out' + : 'block pl-3 pr-4 py-2 border-l-4 border-transparent text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out', + })} + > + {link.label} + </Link> + ))} + </div> + </div> + </nav> + ); +} diff --git a/src/components/Transition.tsx b/src/components/Transition.tsx index 8347336..d424384 100644 --- a/src/components/Transition.tsx +++ b/src/components/Transition.tsx @@ -24,6 +24,8 @@ function CSSTransition({ leaveTo = '', appear, children, + isParent, + timeout, }) { const enterClasses = enter.split(' ').filter(s => s.length); const enterFromClasses = enterFrom.split(' ').filter(s => s.length); @@ -45,9 +47,20 @@ function CSSTransition({ appear={appear} unmountOnExit in={show} - addEndListener={(node, done) => { - node.addEventListener('transitionend', done, false); - }} + addEndListener={ + isParent && timeout + ? undefined + : (node, done) => { + node.addEventListener( + 'transitionend', + e => { + if (node === e.target) done(); + }, + false + ); + } + } + timeout={isParent ? timeout : undefined} onEnter={node => { addClasses(node, [...enterClasses, ...enterFromClasses]); }} @@ -85,6 +98,7 @@ function Transition({ show, appear, ...rest }: any) { <CSSTransition appear={parent.appear || !parent.isInitialRender} show={parent.show} + isParent={false} {...rest} /> ); @@ -102,7 +116,7 @@ function Transition({ show, appear, ...rest }: any) { > {/* // @ts-ignore*/} - <CSSTransition appear={appear} show={show} {...rest} /> + <CSSTransition appear={appear} show={show} isParent={true} {...rest} /> </TransitionContext.Provider> ); } diff --git a/src/components/markdown/CodeBlock.tsx b/src/components/markdown/CodeBlock.tsx deleted file mode 100644 index 7e76652..0000000 --- a/src/components/markdown/CodeBlock.tsx +++ /dev/null @@ -1,36 +0,0 @@ -// File taken from https://github.com/FormidableLabs/prism-react-renderer/issues/54 - -import * as React from 'react'; -import Highlight, { defaultProps } from 'prism-react-renderer'; -import vsDark from 'prism-react-renderer/themes/vsDark'; - -export default ({ children, className }) => { - if (className === undefined) { - // no styling, just a regular pre tag - return <pre className="rounded bg-gray-100 p-4">{children}</pre>; - } - const language = className.replace(/language-/, ''); - return ( - <Highlight - {...defaultProps} - code={children.trim()} - language={language} - theme={vsDark} - > - {({ className, style, tokens, getLineProps, getTokenProps }) => ( - <pre - className={'rounded ' + className} - style={{ ...style, padding: '20px' }} - > - {tokens.map((line, i) => ( - <div key={i} {...getLineProps({ line, key: i })}> - {line.map((token, key) => ( - <span key={key} {...getTokenProps({ token, key })} /> - ))} - </div> - ))} - </pre> - )} - </Highlight> - ); -}; diff --git a/src/components/markdown/CodeBlock/CodeBlock.tsx b/src/components/markdown/CodeBlock/CodeBlock.tsx new file mode 100644 index 0000000..940e347 --- /dev/null +++ b/src/components/markdown/CodeBlock/CodeBlock.tsx @@ -0,0 +1,48 @@ +// File taken from https://github.com/FormidableLabs/prism-react-renderer/issues/54 + +import * as React from 'react'; +import Highlight from './SyntaxHighlighting/Highlight'; +import vsDark from 'prism-react-renderer/themes/vsDark'; +import Prism from './SyntaxHighlighting/prism'; +import { useState } from 'react'; + +export default ({ children, className }) => { + if (className === undefined) { + // no styling, just a regular pre tag + return ( + <pre className="-mx-4 sm:-mx-6 lg:mx-0 lg:rounded bg-gray-100 p-4 mb-4 whitespace-pre-wrap break-all"> + {children} + </pre> + ); + } + const language = className.replace(/language-/, ''); + return ( + // @ts-ignore + <Highlight + Prism={Prism as any} + code={children.trim()} + language={language} + theme={vsDark} + > + {({ className, style, tokens, getLineProps, getTokenProps }) => ( + <div className="gatsby-highlight" data-language={language}> + <pre + className={ + '-mx-4 sm:-mx-6 lg:mx-0 lg:rounded whitespace-pre-wrap break-all p-4 mb-4 ' + + className + } + style={{ ...style }} + > + {tokens.map((line, i) => ( + <div key={i} {...getLineProps({ line, key: i })}> + {line.map((token, key) => ( + <span key={key} {...getTokenProps({ token, key })} /> + ))} + </div> + ))} + </pre> + </div> + )} + </Highlight> + ); +}; diff --git a/src/components/markdown/CodeBlock/SyntaxHighlighting/Highlight.js b/src/components/markdown/CodeBlock/SyntaxHighlighting/Highlight.js new file mode 100644 index 0000000..af8db8a --- /dev/null +++ b/src/components/markdown/CodeBlock/SyntaxHighlighting/Highlight.js @@ -0,0 +1,353 @@ +/* eslint-disable */ + +// taken from https://github.com/FormidableLabs/prism-react-renderer +// really dumb issue: when importing from node_modules, gatsby/webpack doesn't tree-shake properly... + +import { Component } from 'react'; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true, + }); + } else { + obj[key] = value; + } + + return obj; +} + +function _extends() { + _extends = + Object.assign || + function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); +} + +var newlineRe = /\r\n|\r|\n/; // Empty lines need to contain a single empty token, denoted with { empty: true } + +var normalizeEmptyLines = function (line) { + if (line.length === 0) { + line.push({ + types: ['plain'], + content: '', + empty: true, + }); + } else if (line.length === 1 && line[0].content === '') { + line[0].empty = true; + } +}; + +var appendTypes = function (types, add) { + var typesSize = types.length; + + if (typesSize > 0 && types[typesSize - 1] === add) { + return types; + } + + return types.concat(add); +}; // Takes an array of Prism's tokens and groups them by line, turning plain +// strings into tokens as well. Tokens can become recursive in some cases, +// which means that their types are concatenated. Plain-string tokens however +// are always of type "plain". +// This is not recursive to avoid exceeding the call-stack limit, since it's unclear +// how nested Prism's tokens can become + +var normalizeTokens = function (tokens) { + var typeArrStack = [[]]; + var tokenArrStack = [tokens]; + var tokenArrIndexStack = [0]; + var tokenArrSizeStack = [tokens.length]; + var i = 0; + var stackIndex = 0; + var currentLine = []; + var acc = [currentLine]; + + while (stackIndex > -1) { + while ( + (i = tokenArrIndexStack[stackIndex]++) < tokenArrSizeStack[stackIndex] + ) { + var content = void 0; + var types = typeArrStack[stackIndex]; + var tokenArr = tokenArrStack[stackIndex]; + var token = tokenArr[i]; // Determine content and append type to types if necessary + + if (typeof token === 'string') { + types = stackIndex > 0 ? types : ['plain']; + content = token; + } else { + types = appendTypes(types, token.type); + + if (token.alias) { + types = appendTypes(types, token.alias); + } + + content = token.content; + } // If token.content is an array, increase the stack depth and repeat this while-loop + + if (typeof content !== 'string') { + stackIndex++; + typeArrStack.push(types); + tokenArrStack.push(content); + tokenArrIndexStack.push(0); + tokenArrSizeStack.push(content.length); + continue; + } // Split by newlines + + var splitByNewlines = content.split(newlineRe); + var newlineCount = splitByNewlines.length; + currentLine.push({ + types: types, + content: splitByNewlines[0], + }); // Create a new line for each string on a new line + + for (var i$1 = 1; i$1 < newlineCount; i$1++) { + normalizeEmptyLines(currentLine); + acc.push((currentLine = [])); + currentLine.push({ + types: types, + content: splitByNewlines[i$1], + }); + } + } // Decreate the stack depth + + stackIndex--; + typeArrStack.pop(); + tokenArrStack.pop(); + tokenArrIndexStack.pop(); + tokenArrSizeStack.pop(); + } + + normalizeEmptyLines(currentLine); + return acc; +}; + +var themeToDict = function (theme, language) { + var plain = theme.plain; // $FlowFixMe + + var base = Object.create(null); + var themeDict = theme.styles.reduce(function (acc, themeEntry) { + var languages = themeEntry.languages; + var style = themeEntry.style; + + if (languages && !languages.includes(language)) { + return acc; + } + + themeEntry.types.forEach(function (type) { + // $FlowFixMe + var accStyle = _extends({}, acc[type], style); + + acc[type] = accStyle; + }); + return acc; + }, base); // $FlowFixMe + + themeDict.root = plain; // $FlowFixMe + + themeDict.plain = _extends({}, plain, { + backgroundColor: null, + }); + return themeDict; +}; + +function objectWithoutProperties(obj, exclude) { + var target = {}; + + for (var k in obj) + if ( + Object.prototype.hasOwnProperty.call(obj, k) && + exclude.indexOf(k) === -1 + ) + target[k] = obj[k]; + + return target; +} + +var Highlight = + /*@__PURE__*/ + (function (Component) { + function Highlight() { + var this$1 = this; + var args = [], + len = arguments.length; + + while (len--) args[len] = arguments[len]; + + Component.apply(this, args); + + _defineProperty(this, 'getThemeDict', function (props) { + if ( + this$1.themeDict !== undefined && + props.theme === this$1.prevTheme && + props.language === this$1.prevLanguage + ) { + return this$1.themeDict; + } + + this$1.prevTheme = props.theme; + this$1.prevLanguage = props.language; + var themeDict = props.theme + ? themeToDict(props.theme, props.language) + : undefined; + return (this$1.themeDict = themeDict); + }); + + _defineProperty(this, 'getLineProps', function (ref) { + var key = ref.key; + var className = ref.className; + var style = ref.style; + var rest$1 = objectWithoutProperties(ref, [ + 'key', + 'className', + 'style', + 'line', + ]); + var rest = rest$1; + + var output = _extends({}, rest, { + className: 'token-line', + style: undefined, + key: undefined, + }); + + var themeDict = this$1.getThemeDict(this$1.props); + + if (themeDict !== undefined) { + output.style = themeDict.plain; + } + + if (style !== undefined) { + output.style = + output.style !== undefined + ? _extends({}, output.style, style) + : style; + } + + if (key !== undefined) { + output.key = key; + } + + if (className) { + output.className += ' ' + className; + } + + return output; + }); + + _defineProperty(this, 'getStyleForToken', function (ref) { + var types = ref.types; + var empty = ref.empty; + var typesSize = types.length; + var themeDict = this$1.getThemeDict(this$1.props); + + if (themeDict === undefined) { + return undefined; + } else if (typesSize === 1 && types[0] === 'plain') { + return empty + ? { + display: 'inline-block', + } + : undefined; + } else if (typesSize === 1 && !empty) { + return themeDict[types[0]]; + } + + var baseStyle = empty + ? { + display: 'inline-block', + } + : {}; // $FlowFixMe + + var typeStyles = types.map(function (type) { + return themeDict[type]; + }); + return Object.assign.apply(Object, [baseStyle].concat(typeStyles)); + }); + + _defineProperty(this, 'getTokenProps', function (ref) { + var key = ref.key; + var className = ref.className; + var style = ref.style; + var token = ref.token; + var rest$1 = objectWithoutProperties(ref, [ + 'key', + 'className', + 'style', + 'token', + ]); + var rest = rest$1; + + var output = _extends({}, rest, { + className: 'token ' + token.types.join(' '), + children: token.content, + style: this$1.getStyleForToken(token), + key: undefined, + }); + + if (style !== undefined) { + output.style = + output.style !== undefined + ? _extends({}, output.style, style) + : style; + } + + if (key !== undefined) { + output.key = key; + } + + if (className) { + output.className += ' ' + className; + } + + return output; + }); + } + + if (Component) Highlight.__proto__ = Component; + Highlight.prototype = Object.create(Component && Component.prototype); + Highlight.prototype.constructor = Highlight; + + Highlight.prototype.render = function render() { + var ref = this.props; + var Prism = ref.Prism; + var language = ref.language; + var code = ref.code; + var children = ref.children; + var themeDict = this.getThemeDict(this.props); + var grammar = Prism.languages[language]; + var mixedTokens = + grammar !== undefined + ? Prism.tokenize(code, grammar, language) + : [code]; + var tokens = normalizeTokens(mixedTokens); + return children({ + tokens: tokens, + className: 'prism-code language-' + language, + style: themeDict !== undefined ? themeDict.root : {}, + getLineProps: this.getLineProps, + getTokenProps: this.getTokenProps, + }); + }; + + return Highlight; + })(Component); + +export default Highlight; +// export { defaultProps }; diff --git a/src/components/markdown/CodeBlock/SyntaxHighlighting/prism.js b/src/components/markdown/CodeBlock/SyntaxHighlighting/prism.js new file mode 100644 index 0000000..df45796 --- /dev/null +++ b/src/components/markdown/CodeBlock/SyntaxHighlighting/prism.js @@ -0,0 +1,645 @@ +/* eslint-disable */ + +/** + * Prism: Lightweight, robust, elegant syntax highlighting + * MIT license http://www.opensource.org/licenses/mit-license.php/ + * @author Lea Verou http://lea.verou.me + */ + +/** + * prism-react-renderer: + * This file has been modified to remove: + * - globals and window dependency + * - worker support + * - highlightAll and other element dependent methods + * - _.hooks helpers + * - UMD/node-specific hacks + * It has also been run through prettier + */ + +/** + * me (thecodingwizard): + * - added C++, Java, Python syntax highlighting manually + * - oops this is really dumb but I couldn't get tree-shaking to work properly when importing from node_modules >:-( + */ + +var Prism = (function () { + // Private helper vars + var lang = /\blang(?:uage)?-([\w-]+)\b/i; + var uniqueId = 0; + + var _ = { + util: { + encode: function (tokens) { + if (tokens instanceof Token) { + return new Token( + tokens.type, + _.util.encode(tokens.content), + tokens.alias + ); + } else if (_.util.type(tokens) === 'Array') { + return tokens.map(_.util.encode); + } else { + return tokens + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/\u00a0/g, ' '); + } + }, + + type: function (o) { + return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1]; + }, + + objId: function (obj) { + if (!obj['__id']) { + Object.defineProperty(obj, '__id', { value: ++uniqueId }); + } + return obj['__id']; + }, + + // Deep clone a language definition (e.g. to extend it) + clone: function (o, visited) { + var type = _.util.type(o); + visited = visited || {}; + + switch (type) { + case 'Object': + if (visited[_.util.objId(o)]) { + return visited[_.util.objId(o)]; + } + var clone = {}; + visited[_.util.objId(o)] = clone; + + for (var key in o) { + if (o.hasOwnProperty(key)) { + clone[key] = _.util.clone(o[key], visited); + } + } + + return clone; + + case 'Array': + if (visited[_.util.objId(o)]) { + return visited[_.util.objId(o)]; + } + var clone = []; + visited[_.util.objId(o)] = clone; + + o.forEach(function (v, i) { + clone[i] = _.util.clone(v, visited); + }); + + return clone; + } + + return o; + }, + }, + + languages: { + extend: function (id, redef) { + var lang = _.util.clone(_.languages[id]); + + for (var key in redef) { + lang[key] = redef[key]; + } + + return lang; + }, + + /** + * Insert a token before another token in a language literal + * As this needs to recreate the object (we cannot actually insert before keys in object literals), + * we cannot just provide an object, we need anobject and a key. + * @param inside The key (or language id) of the parent + * @param before The key to insert before. If not provided, the function appends instead. + * @param insert Object with the key/value pairs to insert + * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted. + */ + insertBefore: function (inside, before, insert, root) { + root = root || _.languages; + var grammar = root[inside]; + + if (arguments.length == 2) { + insert = arguments[1]; + + for (var newToken in insert) { + if (insert.hasOwnProperty(newToken)) { + grammar[newToken] = insert[newToken]; + } + } + + return grammar; + } + + var ret = {}; + + for (var token in grammar) { + if (grammar.hasOwnProperty(token)) { + if (token == before) { + for (var newToken in insert) { + if (insert.hasOwnProperty(newToken)) { + ret[newToken] = insert[newToken]; + } + } + } + + ret[token] = grammar[token]; + } + } + + // Update references in other language definitions + _.languages.DFS(_.languages, function (key, value) { + if (value === root[inside] && key != inside) { + this[key] = ret; + } + }); + + return (root[inside] = ret); + }, + + // Traverse a language definition with Depth First Search + DFS: function (o, callback, type, visited) { + visited = visited || {}; + for (var i in o) { + if (o.hasOwnProperty(i)) { + callback.call(o, i, o[i], type || i); + + if ( + _.util.type(o[i]) === 'Object' && + !visited[_.util.objId(o[i])] + ) { + visited[_.util.objId(o[i])] = true; + _.languages.DFS(o[i], callback, null, visited); + } else if ( + _.util.type(o[i]) === 'Array' && + !visited[_.util.objId(o[i])] + ) { + visited[_.util.objId(o[i])] = true; + _.languages.DFS(o[i], callback, i, visited); + } + } + } + }, + }, + + plugins: {}, + + highlight: function (text, grammar, language) { + var env = { + code: text, + grammar: grammar, + language: language, + }; + env.tokens = _.tokenize(env.code, env.grammar); + return Token.stringify(_.util.encode(env.tokens), env.language); + }, + + matchGrammar: function ( + text, + strarr, + grammar, + index, + startPos, + oneshot, + target + ) { + var Token = _.Token; + + for (var token in grammar) { + if (!grammar.hasOwnProperty(token) || !grammar[token]) { + continue; + } + + if (token == target) { + return; + } + + var patterns = grammar[token]; + patterns = _.util.type(patterns) === 'Array' ? patterns : [patterns]; + + for (var j = 0; j < patterns.length; ++j) { + var pattern = patterns[j], + inside = pattern.inside, + lookbehind = !!pattern.lookbehind, + greedy = !!pattern.greedy, + lookbehindLength = 0, + alias = pattern.alias; + + if (greedy && !pattern.pattern.global) { + // Without the global flag, lastIndex won't work + var flags = pattern.pattern.toString().match(/[imuy]*$/)[0]; + pattern.pattern = RegExp(pattern.pattern.source, flags + 'g'); + } + + pattern = pattern.pattern || pattern; + + // Don’t cache length as it changes during the loop + for ( + var i = index, pos = startPos; + i < strarr.length; + pos += strarr[i].length, ++i + ) { + var str = strarr[i]; + + if (strarr.length > text.length) { + // Something went terribly wrong, ABORT, ABORT! + return; + } + + if (str instanceof Token) { + continue; + } + + if (greedy && i != strarr.length - 1) { + pattern.lastIndex = pos; + var match = pattern.exec(text); + if (!match) { + break; + } + + var from = match.index + (lookbehind ? match[1].length : 0), + to = match.index + match[0].length, + k = i, + p = pos; + + for ( + var len = strarr.length; + k < len && + (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); + ++k + ) { + p += strarr[k].length; + // Move the index i to the element in strarr that is closest to from + if (from >= p) { + ++i; + pos = p; + } + } + + // If strarr[i] is a Token, then the match starts inside another Token, which is invalid + if (strarr[i] instanceof Token) { + continue; + } + + // Number of tokens to delete and replace with the new match + delNum = k - i; + str = text.slice(pos, p); + match.index -= pos; + } else { + pattern.lastIndex = 0; + + var match = pattern.exec(str), + delNum = 1; + } + + if (!match) { + if (oneshot) { + break; + } + + continue; + } + + if (lookbehind) { + lookbehindLength = match[1] ? match[1].length : 0; + } + + var from = match.index + lookbehindLength, + match = match[0].slice(lookbehindLength), + to = from + match.length, + before = str.slice(0, from), + after = str.slice(to); + + var args = [i, delNum]; + + if (before) { + ++i; + pos += before.length; + args.push(before); + } + + var wrapped = new Token( + token, + inside ? _.tokenize(match, inside) : match, + alias, + match, + greedy + ); + + args.push(wrapped); + + if (after) { + args.push(after); + } + + Array.prototype.splice.apply(strarr, args); + + if (delNum != 1) + _.matchGrammar(text, strarr, grammar, i, pos, true, token); + + if (oneshot) break; + } + } + } + }, + + hooks: { + add: function () {}, + }, + + tokenize: function (text, grammar, language) { + var strarr = [text]; + + var rest = grammar.rest; + + if (rest) { + for (var token in rest) { + grammar[token] = rest[token]; + } + + delete grammar.rest; + } + + _.matchGrammar(text, strarr, grammar, 0, 0, false); + + return strarr; + }, + }; + + var Token = (_.Token = function (type, content, alias, matchedStr, greedy) { + this.type = type; + this.content = content; + this.alias = alias; + // Copy of the full string this token was created from + this.length = (matchedStr || '').length | 0; + this.greedy = !!greedy; + }); + + Token.stringify = function (o, language, parent) { + if (typeof o == 'string') { + return o; + } + + if (_.util.type(o) === 'Array') { + return o + .map(function (element) { + return Token.stringify(element, language, o); + }) + .join(''); + } + + var env = { + type: o.type, + content: Token.stringify(o.content, language, parent), + tag: 'span', + classes: ['token', o.type], + attributes: {}, + language: language, + parent: parent, + }; + + if (o.alias) { + var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias]; + Array.prototype.push.apply(env.classes, aliases); + } + + var attributes = Object.keys(env.attributes) + .map(function (name) { + return ( + name + + '="' + + (env.attributes[name] || '').replace(/"/g, '"') + + '"' + ); + }) + .join(' '); + + return ( + '<' + + env.tag + + ' class="' + + env.classes.join(' ') + + '"' + + (attributes ? ' ' + attributes : '') + + '>' + + env.content + + '</' + + env.tag + + '>' + ); + }; + + return _; +})(); + +/* "prismjs/components/prism-clike" */ +Prism.languages.clike = { + comment: [ + { + pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/, + lookbehind: true, + }, + { + pattern: /(^|[^\\:])\/\/.*/, + lookbehind: true, + greedy: true, + }, + ], + string: { + pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, + greedy: true, + }, + 'class-name': { + pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i, + lookbehind: true, + inside: { + punctuation: /[.\\]/, + }, + }, + keyword: /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, + boolean: /\b(?:true|false)\b/, + function: /\w+(?=\()/, + number: /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i, + operator: /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/, + punctuation: /[{}[\];(),.:]/, +}; + +/* "prismjs/components/prism-c" */ + +Prism.languages.c = Prism.languages.extend('clike', { + 'class-name': { + pattern: /(\b(?:enum|struct)\s+)\w+/, + lookbehind: true, + }, + keyword: /\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/, + operator: />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/, + number: /(?:\b0x(?:[\da-f]+\.?[\da-f]*|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i, +}); +Prism.languages.insertBefore('c', 'string', { + macro: { + // allow for multiline macro definitions + // spaces after the # character compile fine with gcc + pattern: /(^\s*)#\s*[a-z]+(?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im, + lookbehind: true, + alias: 'property', + inside: { + // highlight the path of the include statement as a string + string: { + pattern: /(#\s*include\s*)(?:<.+?>|("|')(?:\\?.)+?\2)/, + lookbehind: true, + }, + // highlight macro directives as keywords + directive: { + pattern: /(#\s*)\b(?:define|defined|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/, + lookbehind: true, + alias: 'keyword', + }, + }, + }, + // highlight predefined macros as constants + constant: /\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/, +}); +delete Prism.languages.c['boolean']; +/* "prismjs/components/prism-cpp" */ + +Prism.languages.cpp = Prism.languages.extend('c', { + 'class-name': { + pattern: /(\b(?:class|enum|struct)\s+)\w+/, + lookbehind: true, + }, + keyword: /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/, + number: { + pattern: /(?:\b0b[01']+|\b0x(?:[\da-f']+\.?[\da-f']*|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+\.?[\d']*|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]*/i, + greedy: true, + }, + operator: />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/, + boolean: /\b(?:true|false)\b/, +}); +Prism.languages.insertBefore('cpp', 'string', { + 'raw-string': { + pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/, + alias: 'string', + greedy: true, + }, +}); + +/* "prismjs/components/prism-java" */ + +(function (Prism) { + var keywords = /\b(?:abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while|var|null|exports|module|open|opens|provides|requires|to|transitive|uses|with)\b/; // based on the java naming conventions + + var className = /\b[A-Z](?:\w*[a-z]\w*)?\b/; + Prism.languages.java = Prism.languages.extend('clike', { + 'class-name': [ + className, // variables and parameters + // this to support class names (or generic parameters) which do not contain a lower case letter (also works for methods) + /\b[A-Z]\w*(?=\s+\w+\s*[;,=())])/, + ], + keyword: keywords, + function: [ + Prism.languages.clike.function, + { + pattern: /(\:\:)[a-z_]\w*/, + lookbehind: true, + }, + ], + number: /\b0b[01][01_]*L?\b|\b0x[\da-f_]*\.?[\da-f_p+-]+\b|(?:\b\d[\d_]*\.?[\d_]*|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i, + operator: { + pattern: /(^|[^.])(?:<<=?|>>>?=?|->|([-+&|])\2|[?:~]|[-+*/%&|^!=<>]=?)/m, + lookbehind: true, + }, + }); + Prism.languages.insertBefore('java', 'class-name', { + annotation: { + alias: 'punctuation', + pattern: /(^|[^.])@\w+/, + lookbehind: true, + }, + namespace: { + pattern: /(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)[a-z]\w*(\.[a-z]\w*)+/, + lookbehind: true, + inside: { + punctuation: /\./, + }, + }, + generics: { + pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/, + inside: { + 'class-name': className, + keyword: keywords, + punctuation: /[<>(),.:]/, + operator: /[?&|]/, + }, + }, + }); +})(Prism); +/* "prismjs/components/prism-python" */ + +Prism.languages.python = { + comment: { + pattern: /(^|[^\\])#.*/, + lookbehind: true, + }, + 'string-interpolation': { + pattern: /(?:f|rf|fr)(?:("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i, + greedy: true, + inside: { + interpolation: { + // "{" <expression> <Optional "!s", "!r", or "!a"> <Optional ":" format specifier> "}" + pattern: /((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/, + lookbehind: true, + inside: { + 'format-spec': { + pattern: /(:)[^:(){}]+(?=}$)/, + lookbehind: true, + }, + 'conversion-option': { + pattern: /![sra](?=[:}]$)/, + alias: 'punctuation', + }, + rest: null, + }, + }, + string: /[\s\S]+/, + }, + }, + 'triple-quoted-string': { + pattern: /(?:[rub]|rb|br)?("""|''')[\s\S]+?\1/i, + greedy: true, + alias: 'string', + }, + string: { + pattern: /(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i, + greedy: true, + }, + function: { + pattern: /((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g, + lookbehind: true, + }, + 'class-name': { + pattern: /(\bclass\s+)\w+/i, + lookbehind: true, + }, + decorator: { + pattern: /(^\s*)@\w+(?:\.\w+)*/i, + lookbehind: true, + alias: ['annotation', 'punctuation'], + inside: { + punctuation: /\./, + }, + }, + keyword: /\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/, + builtin: /\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/, + boolean: /\b(?:True|False|None)\b/, + number: /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i, + operator: /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/, + punctuation: /[{}[\];(),.:]/, +}; +Prism.languages.python['string-interpolation'].inside[ + 'interpolation' +].inside.rest = Prism.languages.python; +Prism.languages.py = Prism.languages.python; + +export default Prism; diff --git a/src/components/markdown/HTMLComponents.tsx b/src/components/markdown/HTMLComponents.tsx new file mode 100644 index 0000000..1819911 --- /dev/null +++ b/src/components/markdown/HTMLComponents.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import CodeBlock from './CodeBlock/CodeBlock'; + +const OffsetAnchor = ({ id, ...props }) => ( + <span + id={id} + {...props} + className="absolute" + style={{ bottom: '100px', height: '2px' }} + /> +); + +const HTMLComponents = { + table: ({ className, ...props }) => ( + <table + {...props} + className={classNames( + 'text-base border-gray-600 no-bottom-margin', + className + )} + /> + ), + th: ({ className, ...props }) => ( + <th {...props} className={classNames('border py-1 px-3', className)} /> + ), + td: ({ className, ...props }) => ( + <td {...props} className={classNames('border py-1 px-3', className)} /> + ), + h1: ({ id, children, ...props }) => ( + <h1 + {...props} + className="leading-tight text-4xl font-bold mb-4 mt-12 text-gray-700" + > + <OffsetAnchor id={id} /> + {children} + </h1> + ), + h2: ({ id, children, ...props }) => ( + <h2 + className="leading-tight text-3xl font-bold mb-4 mt-12 text-gray-700 relative" + {...props} + > + <OffsetAnchor id={id} /> + {children} + </h2> + ), + h3: ({ id, children, ...props }) => ( + <h3 {...props} className="leading-snug text-2xl font-semibold mb-4 mt-8"> + <OffsetAnchor id={id} /> + {children} + </h3> + ), + h4: ({ id, children, ...props }) => ( + <h4 {...props} className="leading-none text-xl font-semibold mb-2 mt-6"> + <OffsetAnchor id={id} /> + {children} + </h4> + ), + p: props => <p {...props} className="mb-4" />, + 'ol.li': ({ children, ...props }) => ( + <li {...props}> + <div className="flex-1">{children}</div> + </li> + ), + inlineCode: props => ( + <code + {...props} + className="font-mono inline bg-gray-200 rounded px-1 py-05" + /> + ), + code: CodeBlock, +}; + +export default HTMLComponents; diff --git a/src/components/markdown/IncompleteSection.tsx b/src/components/markdown/IncompleteSection.tsx new file mode 100644 index 0000000..d836792 --- /dev/null +++ b/src/components/markdown/IncompleteSection.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; + +export const IncompleteSection = ({ children }) => { + return ( + <div className="p-4 bg-red-50 text-red-800 rounded-md tailwind-alert"> + <div className="flex"> + <div className="flex-shrink-0"> + <svg + className="h-5 w-5 text-red-400" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="ml-3"> + <h3 className="text-sm leading-5 font-medium text-red-800"> + This section is not complete. + </h3> + <div className="mt-2 text-sm leading-5 text-red-700 no-bottom-margin"> + Feel free to file a request to complete this using the "Contact Us" + button. <br /> + <i>{children}</i> + </div> + </div> + </div> + </div> + ); +}; diff --git a/src/components/markdown/Info.tsx b/src/components/markdown/Info.tsx new file mode 100644 index 0000000..241417e --- /dev/null +++ b/src/components/markdown/Info.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; + +const Info = ({ + children, + title, +}: { + children: React.ReactNode; + title: string; +}) => ( + <div className="rounded-md bg-blue-50 p-4 mb-4 tailwind-alert"> + <div className="flex"> + <div className="flex-shrink-0"> + <svg + className="h-5 w-5 text-blue-400" + fill="currentColor" + viewBox="0 0 20 20" + > + <path + fillRule="evenodd" + d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="ml-3 flex-1"> + <h3 className="text-sm leading-5 font-medium text-blue-800 my-0"> + {title} + </h3> + <div className="text-sm leading-5 text-blue-700 mt-2 no-bottom-margin"> + {children} + </div> + </div> + </div> + </div> +); + +export default Info; diff --git a/src/components/markdown/LanguageSection.tsx b/src/components/markdown/LanguageSection.tsx new file mode 100644 index 0000000..feb14e8 --- /dev/null +++ b/src/components/markdown/LanguageSection.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import UserDataContext, { + LANGUAGE_LABELS, +} from '../../context/UserDataContext'; +import { useContext } from 'react'; + +export const LanguageSection = props => { + const { lang } = useContext(UserDataContext); + + let sections = {}; + React.Children.map(props.children, child => { + let type = child.props.mdxType; + if (type === 'CPPSection') sections['cpp'] = child; + if (type === 'JavaSection') sections['java'] = child; + if (type === 'PySection') sections['py'] = child; + }); + + if (lang === 'showAll') { + return ( + <> + {Object.keys(sections).map(lang => ( + <div key={lang}> + <p className="text-lg font-bold">{LANGUAGE_LABELS[lang]}</p> + {sections[lang]} + </div> + ))} + </> + ); + } + + if (!sections.hasOwnProperty(lang)) { + return ( + <div className="p-4 bg-red-50 text-red-800 rounded-md"> + <div className="flex"> + <div className="flex-shrink-0"> + <svg + className="h-5 w-5 text-red-400" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="ml-3"> + <h3 className="text-sm leading-5 font-medium text-red-800"> + This section isn't yet available in your chosen language:{' '} + {LANGUAGE_LABELS[lang]}. + </h3> + <div className="mt-2 text-sm leading-5 text-red-700"> + Please choose a different default language for now. Feel free to + file a request to add support for {LANGUAGE_LABELS[lang]} using + the "Contact Us" button. + </div> + </div> + </div> + </div> + ); + } + return sections[lang]; +}; + +export const CPPSection = props => { + return <>{props.children}</>; +}; + +export const JavaSection = props => { + return <>{props.children}</>; +}; + +export const PySection = props => { + return <>{props.children}</>; +}; diff --git a/src/components/markdown/MDXProvider.tsx b/src/components/markdown/MDXProvider.tsx new file mode 100644 index 0000000..930a2be --- /dev/null +++ b/src/components/markdown/MDXProvider.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { MDXProvider } from '@mdx-js/react'; +import Spoiler from './Spoiler'; +import { ProblemsList } from './ProblemsList/ProblemsList'; +import { Resource, ResourcesList } from './ResourcesList'; +import Asterisk from '../Tooltip/Asterisk'; +import TextTooltip from '../Tooltip/TextTooltip'; +import { + CPPSection, + JavaSection, + LanguageSection, + PySection, +} from './LanguageSection'; +import { IncompleteSection } from './IncompleteSection'; +import Info from './Info'; +import Warning from './Warning'; +import Optional from './Optional'; +import HTMLComponents from './HTMLComponents'; + +const components = { + Spoiler, + Info, + Warning, + Optional, + Problems: ProblemsList, + Resources: ResourcesList, + Resource, + TextTooltip, + LanguageSection, + CPPSection, + JavaSection, + PySection, + IncompleteSection, + Asterisk, + + ...HTMLComponents, +}; + +export default function ({ children }) { + return <MDXProvider components={components}>{children}</MDXProvider>; +} diff --git a/src/components/markdown/Markdown.tsx b/src/components/markdown/Markdown.tsx index c61ce57..3e45dd8 100644 --- a/src/components/markdown/Markdown.tsx +++ b/src/components/markdown/Markdown.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; -import '../../styles/markdown.css'; import 'katex/dist/katex.min.css'; import { MDXRenderer } from 'gatsby-plugin-mdx'; -const Markdown = ({ body, className }) => { +const Markdown = ({ body }) => { return ( - <div className={`markdown ${className}`}> + <div className="markdown"> <MDXRenderer>{body}</MDXRenderer> </div> ); diff --git a/src/components/markdown/Optional.tsx b/src/components/markdown/Optional.tsx new file mode 100644 index 0000000..ee808ae --- /dev/null +++ b/src/components/markdown/Optional.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; + +const Optional = ({ + children, + title, + className, +}: { + children: React.ReactNode; + title?: string; + className?: string; +}) => ( + <div + className={`bg-white overflow-hidden shadow rounded-lg border border-purple-400 mb-4`} + > + <div className="p-4 flex items-center font-medium text-purple-800 bg-purple-50"> + <svg className="h-6 w-6 mr-3" fill="currentColor" viewBox="0 0 20 20"> + <path d="M11 3a1 1 0 10-2 0v1a1 1 0 102 0V3zM15.657 5.757a1 1 0 00-1.414-1.414l-.707.707a1 1 0 001.414 1.414l.707-.707zM18 10a1 1 0 01-1 1h-1a1 1 0 110-2h1a1 1 0 011 1zM5.05 6.464A1 1 0 106.464 5.05l-.707-.707a1 1 0 00-1.414 1.414l.707.707zM5 10a1 1 0 01-1 1H3a1 1 0 110-2h1a1 1 0 011 1zM8 16v-1h4v1a2 2 0 11-4 0zM12 14c.015-.34.208-.646.477-.859a4 4 0 10-4.954 0c.27.213.462.519.476.859h4.002z" /> + </svg> + Optional{title ? `: ${title}` : ''} + </div> + <div className="p-4 pb-0">{children}</div> + </div> +); + +export default Optional; diff --git a/src/components/markdown/Problems.tsx b/src/components/markdown/Problems.tsx deleted file mode 100644 index b09b1b3..0000000 --- a/src/components/markdown/Problems.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import * as React from 'react'; -import { Problem } from '../../../content/models'; - -type ProblemsListComponentProps = { - title?: string; - children?: React.ReactChildren; - problems: Problem[]; -}; - -export function ProblemsListComponent(props: ProblemsListComponentProps) { - console.log(props.problems); - return ( - <div className="flex flex-col"> - <div className="-my-2 py-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8"> - <div className="align-middle inline-block min-w-full shadow overflow-hidden sm:rounded-lg border-b border-gray-200"> - <table className="min-w-full no-markdown"> - <thead> - <tr> - <th className="px-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider"> - Source - </th> - <th className="px-6 py-3 border-b border-gray-200 bg-gray-50" /> - <th className="px-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider"> - Problem Name - </th> - <th className="px-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider"> - Difficulty - </th> - <th className="px-6 py-3 border-b border-gray-200 bg-gray-50" /> - <th className="px-6 py-3 border-b border-gray-200 bg-gray-50" /> - </tr> - </thead> - <tbody className="table-alternating-stripes"> - {props.problems.map(problem => ( - <ProblemComponent problem={problem} /> - ))} - </tbody> - </table> - </div> - </div> - </div> - ); -} - -type ProblemComponentProps = { - problem: Problem; -}; - -export function ProblemComponent(props: ProblemComponentProps) { - const difficultyClasses = { - Intro: 'bg-teal-100 text-teal-800', - Easy: 'bg-yellow-100 text-yellow-800', - Normal: 'bg-green-100 text-green-800', - Hard: 'bg-purple-100 text-purple-800', - 'Very Hard': 'bg-red-100 text-red-800', - Insane: 'bg-blue-100 text-blue-800', - }; - const [showTags, setShowTags] = React.useState(false); - const { problem } = props; - - return ( - <tr> - <td className="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500 font-medium"> - {problem.source} - </td> - <td className="pl-6 w-6 py-4 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900"> - {problem.starred && ( - <svg - className="h-6 w-6 text-blue-700" - fill="currentColor" - viewBox="0 0 20 20" - > - <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"></path> - </svg> - )} - </td> - <td className="px-6 py-4 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900"> - <a href={problem.url}>{problem.name}</a> - </td> - <td className="px-6 py-4 whitespace-no-wrap leading-5"> - {problem.difficulty && ( - <span - className={ - 'px-2 inline-flex text-xs leading-5 font-semibold rounded-full ' + - difficultyClasses[problem.difficulty] - } - > - {problem.difficulty} - </span> - )} - </td> - <td className="px-6 py-4 whitespace-no-wrap text-right text-sm leading-5 font-medium"> - {!showTags && ( - <a - href="#" - className="text-indigo-600 hover:text-indigo-900" - onClick={e => { - e.preventDefault(); - setShowTags(true); - }} - > - Show Tags - </a> - )} - {showTags && - (problem.tags && problem.tags.length - ? problem.tags.join(', ') - : 'None')} - </td> - <td className="px-6 py-4 whitespace-no-wrap text-right text-sm leading-5 font-medium"> - {/*{props.children}*/} - <a href="#" className="text-indigo-600 hover:text-indigo-900"> - Show Solution - </a> - </td> - </tr> - ); -} diff --git a/src/components/markdown/ProblemsList/ProblemStatusCheckbox.tsx b/src/components/markdown/ProblemsList/ProblemStatusCheckbox.tsx new file mode 100644 index 0000000..0a047df --- /dev/null +++ b/src/components/markdown/ProblemsList/ProblemStatusCheckbox.tsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import Tooltip from '../../Tooltip/Tooltip'; +import { Problem } from '../../../../content/models'; +import { useContext } from 'react'; +import UserDataContext from '../../../context/UserDataContext'; +import { + NEXT_PROBLEM_STATUS, + PREV_PROBLEM_STATUS, + ProblemProgress, +} from '../../../models/problem'; + +export default function ProblemStatusCheckbox({ + problem, +}: { + problem: Problem; +}) { + const { userProgressOnProblems, setUserProgressOnProblems } = useContext( + UserDataContext + ); + let status: ProblemProgress = + userProgressOnProblems[problem.uniqueID] || 'Not Attempted'; + const color: { [key in ProblemProgress]: string } = { + 'Not Attempted': 'bg-gray-200', + Solving: 'bg-yellow-300', + Solved: 'bg-green-500', + Ignored: 'bg-red-100', + Skipped: 'bg-blue-300', + }; + const handleClick = () => { + setUserProgressOnProblems(problem, NEXT_PROBLEM_STATUS[status]); + }; + const handleRightClick = e => { + e.preventDefault(); + setUserProgressOnProblems(problem, PREV_PROBLEM_STATUS[status]); + }; + return ( + <Tooltip + content={status} + hideOnClick={false} + type="compact" + position="left" + > + <span + onClick={handleClick} + onContextMenu={handleRightClick} + className="inline-block h-6 w-6" + > + <span + className={ + 'inline-block h-6 w-6 rounded-full cursor-pointer transition duration-100 ease-out ' + + color[status] + } + /> + </span> + </Tooltip> + ); +} diff --git a/src/components/markdown/ProblemsList/ProblemsList.tsx b/src/components/markdown/ProblemsList/ProblemsList.tsx new file mode 100644 index 0000000..9a8b119 --- /dev/null +++ b/src/components/markdown/ProblemsList/ProblemsList.tsx @@ -0,0 +1,255 @@ +import * as React from 'react'; +import { Problem } from '../../../../content/models'; +import Transition from '../../Transition'; +import Tooltip from '../../Tooltip/Tooltip'; +import TextTooltip from '../../Tooltip/TextTooltip'; +import { sourceTooltip } from '../ResourcesList'; +import ProblemStatusCheckbox from './ProblemStatusCheckbox'; + +type ProblemsListProps = { + title?: string; + children?: React.ReactChildren; + problems: Problem[]; +}; + +export function ProblemsList(props: ProblemsListProps) { + const [problem, setProblem] = React.useState(null); + const [showModal, setShowModal] = React.useState(false); + return ( + <div className="-mx-4 sm:-mx-6 lg:mx-0"> + <div className="flex flex-col"> + <div className="-my-2 py-2 overflow-x-auto lg:-mx-4 lg:px-4"> + <div className="align-middle inline-block shadow overflow-hidden min-w-full lg:rounded-lg border-b border-gray-200"> + <table className="w-full no-markdown"> + <thead> + <tr> + <th className="pl-4 md:pl-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider"> + Status + </th> + <th className="pl-4 md:px-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider"> + Source + </th> + <th className="pl-4 sm:pl-10 md:pl-12 md:pr-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider whitespace-no-wrap"> + Problem Name + </th> + <th className="pl-4 md:px-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider"> + Difficulty + </th> + <th className="pl-4 md:pl-6 py-3 border-b border-gray-200 bg-gray-50" /> + <th className="pl-4 pr-4 md:px-6 py-3 border-b border-gray-200 bg-gray-50" /> + </tr> + </thead> + <tbody className="table-alternating-stripes"> + {props.problems.map(problem => ( + <ProblemComponent + problem={problem} + onShowSolution={problem => { + setProblem(problem); + setShowModal(true); + }} + key={problem.id} + /> + ))} + </tbody> + </table> + </div> + </div> + </div> + + <Transition show={showModal} timeout={300}> + <div className="fixed z-10 bottom-0 inset-x-0 px-4 pb-6 sm:inset-0 sm:p-0 sm:flex sm:items-center sm:justify-center"> + <Transition + enter="ease-out duration-300" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="ease-in duration-200" + leaveFrom="opacity-100" + leaveTo="opacity-0" + > + <div className="fixed inset-0 transition-opacity"> + <div + className="absolute inset-0 bg-gray-500 opacity-75" + onClick={() => setShowModal(false)} + /> + </div> + </Transition> + + <Transition + enter="ease-out duration-300" + enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" + enterTo="opacity-100 translate-y-0 sm:scale-100" + leave="ease-in duration-200" + leaveFrom="opacity-100 translate-y-0 sm:scale-100" + leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" + > + <div + className="relative bg-white rounded-lg px-4 pt-5 pb-4 overflow-hidden shadow-xl transform transition-all sm:max-w-xl sm:w-full sm:p-6" + role="dialog" + aria-modal="true" + aria-labelledby="modal-headline" + > + <div className="hidden sm:block absolute top-0 right-0 pt-4 pr-4"> + <button + type="button" + className="text-gray-400 hover:text-gray-500 focus:outline-none focus:text-gray-500 transition ease-in-out duration-150" + aria-label="Close" + onClick={() => setShowModal(false)} + > + <svg + className="h-6 w-6" + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + d="M6 18L18 6M6 6l12 12" + /> + </svg> + </button> + </div> + <div className="sm:flex sm:items-start"> + <div className="text-left"> + <h3 + className="text-lg leading-6 font-medium text-gray-900" + id="modal-headline" + > + Solution Sketch: {problem?.name} + </h3> + <div className="mt-4"> + <p className="text-gray-700">{problem?.sketch}</p> + </div> + </div> + </div> + </div> + </Transition> + </div> + </Transition> + </div> + ); +} + +type ProblemComponentProps = { + problem: Problem; + onShowSolution: Function; +}; + +export function ProblemComponent(props: ProblemComponentProps) { + const difficultyClasses = { + 'Very Easy': 'bg-gray-100 text-gray-800', + Easy: 'bg-green-100 text-green-800', + Normal: 'bg-blue-100 text-blue-800', + Hard: 'bg-purple-100 text-purple-800', + 'Very Hard': 'bg-orange-100 text-orange-800', + Insane: 'bg-red-100 text-red-800', + }; + const [showTags, setShowTags] = React.useState(false); + const [isActive, setIsActive] = React.useState(false); + const { problem } = props; + const id = `problem-${problem.uniqueID}`; + React.useEffect(() => { + setIsActive(window && window.location && window.location.hash === '#' + id); + }, []); + return ( + <tr id={id} style={isActive ? { backgroundColor: '#FDFDEA' } : null}> + <td className="pl-4 md:pl-6 whitespace-no-wrap text-sm text-gray-500 font-medium"> + <div + style={{ height: '1.25rem' }} + className="flex items-center justify-center" + > + <ProblemStatusCheckbox problem={problem} /> + </div> + </td> + <td className="pl-4 md:px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-500 font-medium"> + {sourceTooltip.hasOwnProperty(problem.source) ? ( + <TextTooltip content={sourceTooltip[problem.source]}> + {problem.source} + </TextTooltip> + ) : ( + problem.source + )} + </td> + <td className="pl-4 md:px-6 py-4 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900"> + <div className="flex items-center"> + {problem.starred && ( + <Tooltip content="We highly recommend you do all starred problems!"> + <svg + className="h-4 w-4 text-blue-400" + fill="currentColor" + viewBox="0 0 20 20" + > + <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> + </svg> + </Tooltip> + )} + <a + href={problem.url} + className={problem.starred ? 'pl-1 sm:pl-2' : 'sm:pl-6'} + target="_blank" + rel="nofollow noopener noreferrer" + > + {problem.name} + </a> + {problem.isIntro && ( + <span + className={ + 'ml-2 px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-teal-100 text-teal-800' + } + > + Intro + </span> + )} + </div> + </td> + <td className="pl-4 md:px-6 py-4 whitespace-no-wrap leading-5 w-full"> + {problem.difficulty && ( + <span + className={ + 'px-2 inline-flex text-xs leading-5 font-semibold rounded-full ' + + difficultyClasses[problem.difficulty] + } + > + {problem.difficulty} + </span> + )} + </td> + <td className="pl-4 md:pl-6 py-4 whitespace-no-wrap text-right text-sm leading-5 font-medium"> + {!showTags && ( + <a + href="#" + className="text-indigo-600 hover:text-indigo-900" + onClick={e => { + e.preventDefault(); + setShowTags(true); + }} + > + Show Tags + </a> + )} + {showTags && + (problem.tags && problem.tags.length + ? problem.tags.join(', ') + : 'None')} + </td> + <td className="pl-4 pr-4 md:px-6 py-4 whitespace-no-wrap text-right text-sm leading-5 font-medium"> + {problem.sketch && ( + <span + className="text-blue-600 hover:text-blue-900 cursor-pointer" + onClick={() => problem.sketch && props.onShowSolution(problem)} + > + Show Solution + </span> + )} + {!problem.sketch && ( + <Tooltip + content={`We haven't written a solution for this problem yet. If needed, request one using the "Contact Us" button!`} + > + <span className="text-gray-300">Show Solution</span> + </Tooltip> + )} + </td> + </tr> + ); +} diff --git a/src/components/markdown/ResourcesList.tsx b/src/components/markdown/ResourcesList.tsx new file mode 100644 index 0000000..1bbc0c0 --- /dev/null +++ b/src/components/markdown/ResourcesList.tsx @@ -0,0 +1,157 @@ +import * as React from 'react'; +import Tooltip from '../Tooltip/Tooltip'; +import TextTooltip from '../Tooltip/TextTooltip'; +import { useContext } from 'react'; +import UserDataContext from '../../context/UserDataContext'; + +export function ResourcesList(props) { + return ( + <div className="flex flex-col mb-4"> + <div + className={`overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8 -my-2 py-2`} + > + <div + className={`align-middle inline-block min-w-full shadow overflow-hidden rounded-lg border-b border-gray-200`} + > + <table className="min-w-full"> + <thead> + <tr> + <th + colSpan={4} + className={`px-4 sm:px-6 border-b text-left font-medium text-sm uppercase py-3 border-gray-200 bg-purple-50 text-purple-500`} + > + Resources{props.title ? `: ${props.title}` : ''} + </th> + </tr> + </thead> + <tbody className="table-alternating-stripes"> + {props.children} + </tbody> + </table> + </div> + </div> + </div> + ); +} +const books = { + CPH: '/CPH.pdf', + PAPS: 'https://www.csc.kth.se/~jsannemo/slask/main.pdf', + CP1: + 'https://www.comp.nus.edu.sg/~stevenha/myteaching/competitive_programming/cp1.pdf', +}; + +const sources = { + TC: 'https://www.topcoder.com/community/competitive-programming/tutorials/', + CPC: 'https://github.com/SuprDewd/T-414-AFLV/tree/master/', + CF: 'http://codeforces.com/', + 'cp-algo': 'https://cp-algorithms.com/', + CSA: 'https://csacademy.com/lesson/', + GFG: 'https://www.geeksforgeeks.org/', + Benq: 'https://github.com/bqi343/USACO/blob/master/Implementations/content/', + HR: 'https://www.hackerrank.com/', + SO: 'https://stackoverflow.com/', +}; + +export const sourceTooltip = { + AoPS: 'Art of Problem Solving', + CPH: "Book - Competitive Programmer's Handbook", + PAPS: 'Book - Principles of Algorithmic Problem Solving', + IUSACO: 'Book - An Introduction to the USA Computing Olympiad', + CP1: 'Book - Competitive Programming 1', + TC: 'TopCoder', + CPC: + 'Competitive Programming Course (taught at Reykjavík University, Iceland)', + CF: 'CodeForces', + 'cp-algo': 'CP Algorithms', + CSA: 'CS Academy', + GFG: 'Geeks For Geeks', + Benq: 'github.com/bqi343/USACO', + HR: 'HackerRank', + CSES: 'Code Submission Evaluation System (includes CPH problemset)', + HE: 'HackerEarth', + AC: 'AtCoder', + CC: 'CodeChef', + DMOJ: 'Don Mills Online Judge', + SPOJ: 'Sphere Online Judge', + YS: 'Library Checker', + LC: 'LeetCode', + POI: 'Polish Olympiad in Informatics', + SO: 'StackOverflow', + KA: 'KhanAcademy', + USACO: 'USA Computing Olympiad', + '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.', + ZLE: 'kauntaofficial.github.io', +}; + +export function Resource(props) { + const userSettings = useContext(UserDataContext); + + const source = props.source; + let url = props.url; + if (!url) { + if (source === 'IUSACO') { + if (userSettings.lang === 'java') { + url = 'https://darrenyao.com/usacobook/java.pdf'; + } else { + url = 'https://darrenyao.com/usacobook/cpp.pdf'; + } + } else if (source in books) { + url = books[source]; + } else + throw `No URL. Did you make a typo in the source (${source})? Resource title: ${props.title}`; + } else if (!url.startsWith('http')) { + if (source in sources) { + url = sources[source] + url; + } else + throw `URL ${url} is not valid. Did you make a typo in the source (${source}), or in the URL? Resource name: ${props.title}`; + } + return ( + <tr className="block sm:table-row"> + <td className="pl-4 sm:pl-6 pt-4 pb-1 sm:pb-4 whitespace-no-wrap text-sm leading-5 text-gray-500"> + {props.source && ( + <> + {sourceTooltip.hasOwnProperty(props.source) ? ( + <TextTooltip content={sourceTooltip[props.source]}> + {props.source} + </TextTooltip> + ) : ( + props.source + )} + </> + )} + </td> + <td + className={`${ + props.source && 'pl-2 sm:pl-6' + } pr-4 sm:pr-6 pt-4 pb-1 sm:pb-4 whitespace-no-wrap text-sm leading-5 font-medium text-gray-900`} + > + <div className="flex items-center"> + {props.starred && ( + <Tooltip content="You should read all starred resources (unless you already know it) before proceeding!"> + <svg + className="h-4 w-4 text-blue-400" + fill="currentColor" + viewBox="0 0 20 20" + > + <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> + </svg> + </Tooltip> + )} + <a + href={url} + className={props.starred ? 'pl-1 sm:pl-2' : 'sm:pl-6'} + target="_blank" + rel="nofollow noopener noreferrer" + > + {props.title} + </a> + </div> + </td> + <td className="block sm:table-cell sm:w-full px-4 sm:px-6 sm:pt-4 pb-4 text-sm leading-5 text-gray-500"> + {props.children} + </td> + </tr> + ); +} diff --git a/src/components/markdown/SpoilerComponent.tsx b/src/components/markdown/Spoiler.tsx index 248db1d..08c2ec8 100644 --- a/src/components/markdown/SpoilerComponent.tsx +++ b/src/components/markdown/Spoiler.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -const SpoilerComponent = ({ children, title }) => { +const Spoiler = ({ children, title }) => { const [show, setShow] = React.useState(false); return ( @@ -39,9 +39,9 @@ const SpoilerComponent = ({ children, title }) => { {title} </p> - {show && <div className="p-4 pt-0 spoiler-body">{children}</div>} + {show && <div className="px-4 spoiler-body">{children}</div>} </div> ); }; -export default SpoilerComponent; +export default Spoiler; diff --git a/src/components/markdown/Warning.tsx b/src/components/markdown/Warning.tsx new file mode 100644 index 0000000..d069543 --- /dev/null +++ b/src/components/markdown/Warning.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; + +const Warning = ({ children, title }) => ( + <div className="rounded-md bg-yellow-50 p-4 mb-4 tailwind-alert"> + <div className="flex"> + <div className="flex-shrink-0"> + <svg + className="h-5 w-5 text-yellow-400" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="ml-3"> + <h3 className="text-sm leading-5 font-medium text-yellow-800"> + Warning{title ? ': ' + title : '!'} + </h3> + <div className="mt-2 text-sm leading-5 text-yellow-700 no-bottom-margin"> + {children} + </div> + </div> + </div> + </div> +); + +export default Warning; diff --git a/src/context/ModuleLayoutContext.tsx b/src/context/ModuleLayoutContext.tsx new file mode 100644 index 0000000..a8017aa --- /dev/null +++ b/src/context/ModuleLayoutContext.tsx @@ -0,0 +1,10 @@ +import { createContext } from 'react'; +import * as React from 'react'; +import { ModuleInfo, ModuleLinkInfo } from '../models/module'; + +const ModuleLayoutContext = createContext<{ + module: ModuleInfo; + moduleLinks: ModuleLinkInfo[]; +}>(null); + +export default ModuleLayoutContext; diff --git a/src/context/UserDataContext.tsx b/src/context/UserDataContext.tsx new file mode 100644 index 0000000..2f7f3ed --- /dev/null +++ b/src/context/UserDataContext.tsx @@ -0,0 +1,154 @@ +import * as React from 'react'; +import { createContext, useState } from 'react'; +import { Problem } from '../../content/models'; +import { ModuleProgress } from '../models/module'; +import { ProblemProgress } from '../models/problem'; + +export type UserLang = 'showAll' | 'cpp' | 'java' | 'py'; +export const LANGUAGE_LABELS: { [key in UserLang]: string } = { + showAll: 'All', + cpp: 'C++', + java: 'Java', + py: 'Python', +}; + +const UserDataContext = createContext<{ + lang: UserLang; + setLang: (lang: UserLang) => void; + + userProgressOnModules: { [key: string]: ModuleProgress }; + setModuleProgress: (moduleID: string, progress: ModuleProgress) => void; + + userProgressOnProblems: { [key: string]: ProblemProgress }; + setUserProgressOnProblems: ( + problem: Problem, + status: ProblemProgress + ) => void; + + lastViewedModule: string; + setLastViewedModule: (moduleID: string) => void; +}>({ + lang: 'showAll', + setLang: null, + userProgressOnModules: null, + setModuleProgress: null, + userProgressOnProblems: null, + setUserProgressOnProblems: null, + lastViewedModule: null, + setLastViewedModule: null, +}); + +const langKey = 'guide:userData:lang'; +const getLangFromStorage = () => { + let stickyValue = window.localStorage.getItem(langKey); + let v = null; + try { + v = JSON.parse(stickyValue); + } catch (e) { + console.error("Couldn't parse user primary language", e); + } + if (v === 'cpp' || v === 'java' || v === 'py') return v; + return 'cpp'; +}; + +const progressKey = 'guide:userData:progress'; +const getProgressFromStorage = () => { + let stickyValue = window.localStorage.getItem(progressKey); + let v = {}; + try { + v = JSON.parse(stickyValue); + } catch (e) { + console.error("Couldn't parse user progress", e); + } + return v || {}; +}; + +const problemStatusKey = 'guide:userData:problemStatus'; +const getProblemStatusFromStorage = () => { + let stickyValue = window.localStorage.getItem(problemStatusKey); + let v = {}; + try { + v = JSON.parse(stickyValue); + } catch (e) { + console.error("Couldn't parse problem status", e); + } + return v || {}; +}; + +const lastViewedModuleKey = 'guide:userData:lastViewedModule'; +const getLastViewedModuleFromStorage = () => { + let stickyValue = window.localStorage.getItem(lastViewedModuleKey); + let v = null; + try { + v = JSON.parse(stickyValue); + } catch (e) { + console.error("Couldn't parse last viewed module", e); + } + return v || null; +}; + +export const UserDataProvider = ({ children }) => { + const [lang, setLang] = useState<UserLang>('showAll'); + const [userProgress, setUserProgress] = useState<{ + [key: string]: ModuleProgress; + }>({}); + const [problemStatus, setProblemStatus] = useState<{ + [key: string]: ProblemProgress; + }>({}); + const [lastViewedModule, setLastViewedModule] = useState<string>(null); + + React.useEffect(() => { + setLang(getLangFromStorage()); + setUserProgress(getProgressFromStorage()); + setProblemStatus(getProblemStatusFromStorage()); + setLastViewedModule(getLastViewedModuleFromStorage()); + }, []); + + const userData = React.useMemo( + () => ({ + lang: lang as UserLang, + setLang: lang => { + window.localStorage.setItem(langKey, JSON.stringify(lang)); + setLang(lang); + }, + userProgressOnModules: userProgress, + setModuleProgress: (moduleID: string, progress: ModuleProgress) => { + const newProgress = { + ...getProgressFromStorage(), + [moduleID]: progress, + }; + window.localStorage.setItem(progressKey, JSON.stringify(newProgress)); + setUserProgress(newProgress); + }, + userProgressOnProblems: problemStatus, + setUserProgressOnProblems: (problem, status) => { + const newStatus = { + ...getProblemStatusFromStorage(), + [problem.uniqueID]: status, + }; + window.localStorage.setItem( + problemStatusKey, + JSON.stringify(newStatus) + ); + setProblemStatus(newStatus); + }, + lastViewedModule, + setLastViewedModule: moduleID => { + window.localStorage.setItem( + lastViewedModuleKey, + JSON.stringify(moduleID) + ); + setLastViewedModule(moduleID); + }, + }), + [lang, userProgress, problemStatus, lastViewedModule] + ); + + return ( + <UserDataContext.Provider value={userData}> + {children} + </UserDataContext.Provider> + ); +}; + +export default UserDataContext; diff --git a/src/hooks/useActiveHash.tsx b/src/hooks/useActiveHash.tsx new file mode 100644 index 0000000..a8eaea2 --- /dev/null +++ b/src/hooks/useActiveHash.tsx @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react'; + +// taken from https://github.com/gatsbyjs/gatsby/blob/3519e71ee6f94bfbdd936b5a7fa4cab36c970c4d/www/src/hooks/use-active-hash.js#L3 + +export const useActiveHash = (itemIds, rootMargin = undefined) => { + const [activeHash, setActiveHash] = useState(``); + + useEffect(() => { + let isInteresting = {}; + const observer = new IntersectionObserver( + entries => { + entries.forEach(entry => { + isInteresting[entry.target.id] = entry.isIntersecting; + }); + setActiveHash(itemIds.find(id => isInteresting[id])); + }, + { rootMargin: rootMargin || `0% 0% -80% 0%` } + ); + + itemIds.forEach(id => { + observer.observe(document.getElementById(id)); + }); + + return () => { + itemIds.forEach(id => { + let el = document.getElementById(id); + if (el) observer.unobserve(el); + }); + }; + }, [itemIds]); + + return activeHash; +}; diff --git a/src/hooks/useStickyState.tsx b/src/hooks/useStickyState.tsx new file mode 100644 index 0000000..e90a7cf --- /dev/null +++ b/src/hooks/useStickyState.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; + +// source: https://joshwcomeau.com/react/persisting-react-state-in-localstorage/ +// modified to support ssr + +export default function useStickyState(defaultValue, key) { + const [value, setValue] = React.useState(defaultValue); + const initialRender = React.useRef(true); + React.useEffect(() => { + if (initialRender.current) { + const stickyValue = window.localStorage.getItem(key); + if (stickyValue !== null) setValue(JSON.parse(stickyValue)); + + initialRender.current = false; + } else { + window.localStorage.setItem(key, JSON.stringify(value)); + } + }, [key, value]); + return [value, setValue]; +} diff --git a/src/hooks/useWindowDimensions.ts b/src/hooks/useWindowDimensions.ts new file mode 100644 index 0000000..774b54f --- /dev/null +++ b/src/hooks/useWindowDimensions.ts @@ -0,0 +1,46 @@ +import { useState, useEffect } from 'react'; + +function getWindowDimensions() { + const { innerWidth: width, innerHeight: height } = window; + return { + width, + height, + }; +} + +function debounce(func, wait, immediate = false) { + var timeout; + return function () { + var context = this, + args = arguments; + var later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +} + +export default function useWindowDimensions() { + const [windowDimensions, setWindowDimensions] = useState({ + height: 920, + width: 620, + }); + + useEffect(() => { + function handleResize() { + setWindowDimensions(getWindowDimensions()); + } + handleResize(); + + const efficientResize = debounce(handleResize, 250); + + window.addEventListener('resize', efficientResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + return windowDimensions; +} diff --git a/src/html.tsx b/src/html.tsx index f0a944c..f7f2bda 100644 --- a/src/html.tsx +++ b/src/html.tsx @@ -11,6 +11,12 @@ export default function HTML(props) { content="width=device-width, initial-scale=1, shrink-to-fit=no" /> {props.headComponents} + {/* Appzi: Capture Insightful Feedback */} + <script + async + src="https://w.appzi.io/bootstrap/bundle.js?token=iIhbb" + /> + {/* End Appzi */} </head> <body {...props.bodyAttributes}> {props.preBodyComponents} diff --git a/src/mdx-plugins/table-of-contents.ts b/src/mdx-plugins/table-of-contents.ts new file mode 100644 index 0000000..f6fb2c6 --- /dev/null +++ b/src/mdx-plugins/table-of-contents.ts @@ -0,0 +1,127 @@ +const visit = require('unist-util-visit'); + +export default ({ markdownAST }, pluginOptions) => { + // let newChildren = []; + // for (let i = 0; i < markdownAST.children.length; i++) { + // let child = markdownAST.children[i]; + // if (child.type === 'comment') { + // let res = re.exec(child.value); + // console.log(res); + // } + // newChildren.push(child); + // let codeElements = []; + // let activeCodeComment = false; + // for (; i < markdownAST.children.length; i++) { + // let child = markdownAST.children[i]; + // if (child.type === 'jsx' && child.value.startsWith('<code-comment')) + // activeCodeComment = true; + // if (activeCodeCoconst util = require("mdast-util-toc"); + // const yaml = require("js-yaml"); + // + // // convert "in-string" to "inString" + // const strToCamel = str => { + // return str.replace(/-(.)/g, (match, chr) => chr.toUpperCase()); + // }; + // + // // convert "{'in-key': val}" to "{'inKey': val}" + // const keysToCamel = obj => { + // if (obj) { + // const newObj = {}; + // Object.keys(obj).forEach(k => { + // newObj[strToCamel(k)] = obj[k]; + // }); + // return newObj; + // } + // return obj; + // }; + // + // const transformer = (markdownAST, pluginOptions) => { + // // find position of TOC + // const index = markdownAST.children.findIndex( + // node => node.type === "code" && node.lang === "toc" + // ); + // + // // we have no TOC + // if (index === -1) { + // return; + // } + // + // let prefs = { + // tight: false, + // fromHeading: 2, + // toHeading: 6, + // ...keysToCamel(pluginOptions) + // }; + // + // try { + // let parsePrefs = yaml.safeLoad(markdownAST.children[index].value); + // prefs = { ...prefs, ...keysToCamel(parsePrefs) }; + // } catch (e) { + // console.log("Can't parse TOC-Configuration", e); + // } + // + // // this ist the ast we nned consider + // let tocMarkdownAST = { + // ...markdownAST, + // children: [] + // }; + // + // let depth; + // + // // add all headings + // markdownAST.children.forEach(node => { + // if (node.type === "heading" && node.depth > prefs.fromHeading - 1) { + // tocMarkdownAST.children.push(node); + // } + // }); + // + // // calculate TOC + // const result = util(tocMarkdownAST, { + // maxDepth: prefs.toHeading, + // tight: prefs.tight, + // skip: Array.isArray(prefs.exclude) ? prefs.exclude.join("|") : prefs.exclude + // }); + // + // // insert the TOC≤ + // markdownAST.children = [].concat( + // markdownAST.children.slice(0, index), + // { + // type: "html", + // value: '<div class="toc">' + // }, + // result.map, + // { + // type: "html", + // value: "</div>" + // }, + // markdownAST.children.slice(index + 1) + // ); + // }; + // + // module.exports = ({ markdownAST }, pluginOptions) => { + // return transformer(markdownAST, pluginOptions); + // };mment || (child.type === 'code' && child.lang)) { + // codeElements.push(markdownAST.children[i]); + // } else { + // if (codeElements.length > 0) i--; + // break; + // } + // if (child.type === 'jsx' && child.value === '</code-comment>') + // activeCodeComment = false; + // } + // if (codeElements.length > 0) { + // newChildren.push({ + // type: 'jsx', + // value: '<code-section>', + // }); + // for (let e of codeElements) newChildren.push(e); + // newChildren.push({ + // type: 'jsx', + // value: '</code-section>', + // }); + // } else { + // newChildren.push(markdownAST.children[i]); + // } + // } + // markdownAST.children = newChildren; +}; diff --git a/src/models/module.ts b/src/models/module.ts new file mode 100644 index 0000000..aaaad05 --- /dev/null +++ b/src/models/module.ts @@ -0,0 +1,59 @@ +export class ModuleLinkInfo { + public url: string; + + constructor( + public id: string, + public section: string, + public title: string, + public description?: string + ) { + this.url = `/${section}/${id}`; + } +} + +export type ModuleFrequency = null | 0 | 1 | 2 | 3 | 4; + +export type TOCHeading = { + depth: number; + value: string; + slug: string; +}; + +export type TableOfContents = { + cpp: TOCHeading[]; + java: TOCHeading[]; + py: TOCHeading[]; +}; + +export class ModuleInfo extends ModuleLinkInfo { + constructor( + public id: string, + public section: string, + public title: string, + public body: any, + public author: string, + public prerequisites: string[], + public description: string, + public frequency: ModuleFrequency, + public toc: TableOfContents + ) { + super(id, section, title); + } +} + +export type ModuleProgress = + | 'Not Started' + | 'Reading' + | 'Practicing' + | 'Complete' + | 'Skipped' + | 'Ignored'; + +export const ModuleProgressOptions: ModuleProgress[] = [ + 'Not Started', + 'Reading', + 'Practicing', + 'Complete', + 'Skipped', + 'Ignored', +]; diff --git a/src/models/problem.ts b/src/models/problem.ts new file mode 100644 index 0000000..0251b4c --- /dev/null +++ b/src/models/problem.ts @@ -0,0 +1,88 @@ +const sources = { + AC: 'https://atcoder.jp/', + CC: 'https://www.codechef.com/problems/', + CSA: 'https://csacademy.com/contest/archive/task/', + DMOJ: 'https://dmoj.ca/problem/', + SPOJ: 'https://www.spoj.com/problems/', + YS: 'https://judge.yosupo.jp/problem/', + CF: 'https://codeforces.com/', + Bronze: 'http://www.usaco.org/index.php?page=viewproblem2&cpid=', + Silver: 'http://www.usaco.org/index.php?page=viewproblem2&cpid=', + Gold: 'http://www.usaco.org/index.php?page=viewproblem2&cpid=', + 'Old Bronze': 'http://www.usaco.org/index.php?page=viewproblem2&cpid=', + 'Old Silver': 'http://www.usaco.org/index.php?page=viewproblem2&cpid=', + 'Old Gold': 'http://www.usaco.org/index.php?page=viewproblem2&cpid=', + Plat: 'http://www.usaco.org/index.php?page=viewproblem2&cpid=', + Kattis: 'https://open.kattis.com/problems/', + CSES: 'https://cses.fi/problemset/task/', + LC: 'https://leetcode.com/problems/', + ojuz: 'https://oj.uz/problem/view/', + HR: 'https://www.hackerrank.com/', +}; + +export class Problem { + public url: string; + public difficulty: + | 'Very Easy' + | 'Easy' + | 'Normal' + | 'Hard' + | 'Very Hard' + | 'Insane'; + public isIntro: boolean; + + get uniqueID() { + return this.url; + } + + constructor( + public source: string, + public name: string, + public id: string, + labels?: + | 'Very Easy' + | 'Easy' + | 'Normal' + | 'Hard' + | 'Very Hard' + | 'Insane' + | 'Intro|Very Easy' + | 'Intro|Easy' + | 'Intro|Normal' + | 'Intro|Hard' + | 'Intro|Very Hard' + | 'Intro|Insane', + public starred?: boolean, + public tags?: string[], + public sketch?: string + ) { + this.isIntro = labels && labels.includes('Intro|'); + if (labels) labels = labels.replace('Intro|', '') as any; + this.difficulty = labels as any; + if (!id.startsWith('http')) { + if (source in sources) { + this.url = sources[source] + id; + } else + throw `URL ${id} is not valid. Did you make a typo in the problem source (${source}), or in the URL? Problem name: ${name}`; + } else this.url = id; + } +} + +export type ProblemProgress = + | 'Not Attempted' + | 'Solving' + | 'Solved' + | 'Skipped' + | 'Ignored'; + +let options = ['Not Attempted', 'Solving', 'Solved', 'Skipped', 'Ignored']; + +let NEXT_PROBLEM_STATUS: { [key in ProblemProgress]?: ProblemProgress } = {}; +let PREV_PROBLEM_STATUS: { [key in ProblemProgress]?: ProblemProgress } = {}; +for (let i = 0; i < options.length; i++) { + NEXT_PROBLEM_STATUS[options[i]] = options[(i + 1) % options.length]; + PREV_PROBLEM_STATUS[options[i]] = + options[(i - 1 + options.length) % options.length]; +} + +export { NEXT_PROBLEM_STATUS, PREV_PROBLEM_STATUS }; diff --git a/src/module.ts b/src/module.ts deleted file mode 100644 index 9ac05d4..0000000 --- a/src/module.ts +++ /dev/null @@ -1,29 +0,0 @@ -export class ModuleLinkInfo { - public url: string; - - constructor( - public id: string, - public division: string, - public title: string - ) { - this.url = `/${division}/${id}`; - } -} - -export type ModuleFrequency = null | 0 | 1 | 2 | 3 | 4; - -// there's probably a way to do this without the duplicated types... -export class ModuleInfo extends ModuleLinkInfo { - constructor( - public id: string, - public division: string, - public title: string, - public body: any, - public author: string, - public prerequisites: string[], - public description: string, - public frequency: ModuleFrequency - ) { - super(id, division, title); - } -} diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx new file mode 100644 index 0000000..e4c2a52 --- /dev/null +++ b/src/pages/dashboard.tsx @@ -0,0 +1,275 @@ +import * as React from 'react'; +import { graphql, PageProps } from 'gatsby'; +import Layout from '../components/layout'; +import SEO from '../components/seo'; +import DashboardProgress from '../components/Dashboard/DashboardProgress'; +import UserDataContext from '../context/UserDataContext'; +import WelcomeBackBanner from '../components/Dashboard/WelcomeBackBanner'; +import { + moduleIDToSectionMap, + moduleIDToURLMap, + SECTION_LABELS, +} from '../../content/ordering'; +import TopNavigationBar from '../components/TopNavigationBar'; +import ActiveItems, { ActiveItem } from '../components/Dashboard/ActiveItems'; +import getProgressInfo from '../utils/getProgressInfo'; + +export default function DashboardPage(props: PageProps) { + const { modules } = props.data as any; + const moduleIDToName = modules.edges.reduce((acc, cur) => { + acc[cur.node.frontmatter.id] = cur.node.frontmatter.title; + return acc; + }, {}); + const problemIDMap = modules.edges.reduce((acc, cur) => { + cur.node.problems.forEach(problem => { + acc[problem.uniqueID] = { + label: `${problem.source}: ${problem.name}`, + url: `${moduleIDToURLMap[cur.node.frontmatter.id]}/#problem-${ + problem.uniqueID + }`, + starred: problem.starred, + }; + }); + return acc; + }, {}); + const { + lastViewedModule: lastViewedModuleID, + userProgressOnModules, + userProgressOnProblems, + } = React.useContext(UserDataContext); + const lastViewedModuleURL = moduleIDToURLMap[lastViewedModuleID]; + const activeModules: ActiveItem[] = React.useMemo(() => { + return Object.keys(userProgressOnModules) + .filter( + x => + userProgressOnModules[x] === 'Reading' || + userProgressOnModules[x] === 'Practicing' || + userProgressOnModules[x] === 'Skipped' + ) + .map(x => ({ + label: `${SECTION_LABELS[moduleIDToSectionMap[x]]}: ${ + moduleIDToName[x] + }`, + url: moduleIDToURLMap[x], + status: + userProgressOnModules[x] === 'Skipped' ? 'Skipped' : 'In Progress', + })); + }, [userProgressOnModules]); + const activeProblems: ActiveItem[] = React.useMemo(() => { + return Object.keys(userProgressOnProblems) + .filter( + x => + userProgressOnProblems[x] === 'Solving' || + userProgressOnProblems[x] === 'Skipped' + ) + .map(x => ({ + ...problemIDMap[x], + status: userProgressOnProblems[x] as 'Solving' | 'Skipped', + })); + }, [userProgressOnProblems]); + + let allModulesProgressInfo = getProgressInfo( + Object.keys(moduleIDToName), + userProgressOnModules, + ['Complete'], + ['Reading', 'Practicing'], + ['Skipped'], + ['Not Started'] + ); + + const allProblemIDs = Object.keys(problemIDMap); + // const allStarredProblemIDs = allProblemIDs.filter( + // x => problemIDMap[x].starred + // ); + const allProblemsProgressInfo = getProgressInfo( + allProblemIDs, + userProgressOnProblems, + ['Solved'], + ['Solving'], + ['Skipped'], + ['Not Attempted'] + ); + // const allStarredProblemsProgressInfo = getProgressInfo( + // allStarredProblemIDs, + // userProgressOnProblems, + // ['Solved'], + // ['Solving'], + // ['Skipped'], + // ['Not Attempted'] + // ); + + return ( + <Layout> + <SEO title="Dashboard" /> + + <div className="min-h-screen bg-gray-100"> + <TopNavigationBar /> + + <main className="pb-12"> + <div className="max-w-7xl mx-auto mb-4"> + <div className="flex overflow-x-auto lg:px-8 lg:pt-10 pb-6"> + <WelcomeBackBanner + lastViewedModuleURL={lastViewedModuleURL} + lastViewedModuleLabel={moduleIDToName[lastViewedModuleID]} + /> + </div> + </div> + <div className="max-w-7xl mx-auto sm:px-6 lg:px-8 lg:grid lg:grid-cols-2 lg:gap-8"> + {activeProblems.length > 0 && ( + <div> + <ActiveItems type="problems" items={activeProblems} /> + </div> + )} + {activeModules.length > 0 && ( + <div> + <ActiveItems type="modules" items={activeModules} /> + </div> + )} + </div> + {/*<header>*/} + {/* <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">*/} + {/* <h1 className="text-3xl font-bold leading-tight text-gray-900">*/} + {/* Announcements*/} + {/* </h1>*/} + {/* </div>*/} + {/*</header>*/} + {/*<div className="max-w-7xl mx-auto mb-8">*/} + {/* <div className="flex overflow-x-auto sm:px-6 lg:px-8 py-4 lg:grid lg:grid-cols-2 lg:gap-8">*/} + {/* <div className="bg-white shadow hover:shadow-lg transition duration-150 ease-in-out sm:rounded-lg">*/} + {/* <div className="px-4 py-5 sm:p-6 cursor-pointer">*/} + {/* <p className="text-sm leading-5 text-gray-500">*/} + {/* <time dateTime="2020-07-18">July 18, 2020</time>*/} + {/* </p>*/} + {/* <h3 className="mt-2 text-xl leading-7 font-semibold text-gray-900">*/} + {/* Looking for Contributors!*/} + {/* </h3>*/} + {/* <p className="mt-3 text-base leading-6 text-gray-500">*/} + {/* Welcome to the USACO Guide! We're still in pre-release mode,*/} + {/* so things may be a bit rough around the edges. Learn more*/} + {/* about what this means, and how you can help contribute!*/} + {/* </p>*/} + {/* <div className="mt-3">*/} + {/* <span className="text-base leading-6 font-semibold text-indigo-600 hover:text-indigo-500 transition ease-in-out duration-150">*/} + {/* Continue Reading*/} + {/* </span>*/} + {/* </div>*/} + {/* </div>*/} + {/* </div>*/} + {/* </div>*/} + {/*</div>*/} + <header> + <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> + <h1 className="text-3xl font-bold leading-tight text-gray-900"> + Statistics + </h1> + </div> + </header> + <div className="max-w-7xl mx-auto"> + <div className="sm:px-6 lg:px-8 py-4 lg:grid lg:grid-cols-2 lg:gap-8 space-y-8 lg:space-y-0"> + <div className="space-y-8"> + <div className="bg-white shadow sm:rounded-lg"> + <div className="px-4 py-5 sm:p-6"> + <h3 className="text-lg leading-6 font-medium text-gray-900"> + All Modules + </h3> + <div className="mt-6"> + <DashboardProgress + {...allModulesProgressInfo} + total={Object.keys(moduleIDToName).length} + /> + </div> + </div> + </div> + {/*<div className="bg-white shadow sm:rounded-lg">*/} + {/* <div className="px-4 py-5 sm:p-6">*/} + {/* <h3 className="text-lg leading-6 font-medium text-gray-900">*/} + {/* All Starred Problems*/} + {/* </h3>*/} + {/* <div className="mt-6">*/} + {/* <DashboardProgress*/} + {/* {...allStarredProblemsProgressInfo}*/} + {/* total={Object.keys(allStarredProblemIDs).length}*/} + {/* />*/} + {/* </div>*/} + {/* </div>*/} + {/*</div>*/} + </div> + <div className="space-y-8"> + <div className="bg-white shadow sm:rounded-lg order-6"> + <div className="px-4 py-5 sm:p-6"> + <h3 className="text-lg leading-6 font-medium text-gray-900"> + All Problems + </h3> + <div className="mt-6"> + <DashboardProgress + {...allProblemsProgressInfo} + total={Object.keys(allProblemIDs).length} + /> + </div> + </div> + </div> + {/*<div className="bg-white shadow sm:rounded-lg overflow-hidden row-span-2 flex flex-col">*/} + {/* <div className="px-4 pt-5 sm:px-6 sm:pt-6 pb-4">*/} + {/* <h3 className="text-lg leading-6 font-medium text-gray-900">*/} + {/* 🔥 6 Day Streak: Keep it up!*/} + {/* </h3>*/} + {/* <div className="mt-2 max-w-xl text-sm leading-5 text-gray-500">*/} + {/* <p>*/} + {/* You've visited this guide for 6 consecutive days. Enjoy*/} + {/* this cute cow photo as a reward!*/} + {/* </p>*/} + {/* </div>*/} + {/* </div>*/} + {/* <img*/} + {/* className="h-64 w-full object-cover"*/} + {/* src="https://66.media.tumblr.com/709acf5805b63bf412dd5cf8d6e34803/tumblr_oplgjdcYJl1sgqqono1_500.jpg"*/} + {/* alt="Cow"*/} + {/* />*/} + {/*</div>*/} + {/*<div className="bg-white shadow sm:rounded-lg">*/} + {/* <div className="px-4 py-5 sm:p-6">*/} + {/* <h3 className="text-lg leading-6 font-medium text-gray-900">*/} + {/* Section Breakdown*/} + {/* </h3>*/} + {/* <div className="mt-2 max-w-xl text-sm leading-5 text-gray-500">*/} + {/* <p>Below is your progress on modules for each section.</p>*/} + {/* </div>*/} + {/* <div className="mt-4">*/} + {/* <SectionProgressBar title="Intro" />*/} + {/* <SectionProgressBar title="Bronze" />*/} + {/* <SectionProgressBar title="Silver" />*/} + {/* <SectionProgressBar title="Gold" />*/} + {/* <SectionProgressBar title="Plat" />*/} + {/* <SectionProgressBar title="Advanced" />*/} + {/* </div>*/} + {/* </div>*/} + {/*</div>*/} + </div> + </div> + </div> + </main> + </div> + </Layout> + ); +} + +export const pageQuery = graphql` + query { + modules: allMdx { + edges { + node { + frontmatter { + title + id + } + problems { + uniqueID + source + name + starred + } + } + } + } + } +`; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 3add4f0..10ae08a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,7 +1,129 @@ import * as React from 'react'; -import { Link, PageProps } from 'gatsby'; +import { graphql, Link, PageProps } from 'gatsby'; +import Img from 'gatsby-image'; import Layout from '../components/layout'; import SEO from '../components/seo'; +import { Author, Authors } from '../../content/authors/authors'; + +const AuthorCard = ({ + author, + gatsbyImage, +}: { + author: Author; + gatsbyImage: any; +}) => { + const socialMedia = { + email: { + icon: ( + <svg + className="h-6 w-6" + fill="none" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + viewBox="0 0 24 24" + stroke="currentColor" + > + <path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /> + </svg> + ), + link: x => `mailto:${x}`, + }, + facebook: { + icon: ( + <svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24"> + <path + fillRule="evenodd" + d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" + clipRule="evenodd" + /> + </svg> + ), + link: x => `https://www.facebook.com/${x}`, + }, + linkedin: { + icon: ( + <svg className="h-6 w-6" fill="currentColor" viewBox="0 0 1792 1792"> + <path d="M365 1414h231v-694h-231v694zm246-908q-1-52-36-86t-93-34-94.5 34-36.5 86q0 51 35.5 85.5t92.5 34.5h1q59 0 95-34.5t36-85.5zm585 908h231v-398q0-154-73-233t-193-79q-136 0-209 117h2v-101h-231q3 66 0 694h231v-388q0-38 7-56 15-35 45-59.5t74-24.5q116 0 116 157v371zm468-998v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z" /> + </svg> + ), + link: x => `https://www.linkedin.com/in/${x}`, + }, + github: { + icon: ( + <svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24"> + <path + fillRule="evenodd" + d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" + clipRule="evenodd" + /> + </svg> + ), + link: x => `https://github.com/${x}`, + }, + codeforces: { + icon: ( + <svg viewBox="0 0 24 24" className="h-6 w-6" fill="currentColor"> + <path d="M4.5 7.5C5.328 7.5 6 8.172 6 9v10.5c0 .828-.672 1.5-1.5 1.5h-3C.673 21 0 20.328 0 19.5V9c0-.828.673-1.5 1.5-1.5h3zm9-4.5c.828 0 1.5.672 1.5 1.5v15c0 .828-.672 1.5-1.5 1.5h-3c-.827 0-1.5-.672-1.5-1.5v-15c0-.828.673-1.5 1.5-1.5h3zm9 7.5c.828 0 1.5.672 1.5 1.5v7.5c0 .828-.672 1.5-1.5 1.5h-3c-.828 0-1.5-.672-1.5-1.5V12c0-.828.672-1.5 1.5-1.5h3z" /> + </svg> + ), + link: x => `https://codeforces.com/profile/${x}`, + }, + website: { + icon: ( + <svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24"> + <path d="M16.36,14C16.44,13.34 16.5,12.68 16.5,12C16.5,11.32 16.44,10.66 16.36,10H19.74C19.9,10.64 20,11.31 20,12C20,12.69 19.9,13.36 19.74,14M14.59,19.56C15.19,18.45 15.65,17.25 15.97,16H18.92C17.96,17.65 16.43,18.93 14.59,19.56M14.34,14H9.66C9.56,13.34 9.5,12.68 9.5,12C9.5,11.32 9.56,10.65 9.66,10H14.34C14.43,10.65 14.5,11.32 14.5,12C14.5,12.68 14.43,13.34 14.34,14M12,19.96C11.17,18.76 10.5,17.43 10.09,16H13.91C13.5,17.43 12.83,18.76 12,19.96M8,8H5.08C6.03,6.34 7.57,5.06 9.4,4.44C8.8,5.55 8.35,6.75 8,8M5.08,16H8C8.35,17.25 8.8,18.45 9.4,19.56C7.57,18.93 6.03,17.65 5.08,16M4.26,14C4.1,13.36 4,12.69 4,12C4,11.31 4.1,10.64 4.26,10H7.64C7.56,10.66 7.5,11.32 7.5,12C7.5,12.68 7.56,13.34 7.64,14M12,4.03C12.83,5.23 13.5,6.57 13.91,8H10.09C10.5,6.57 11.17,5.23 12,4.03M18.92,8H15.97C15.65,6.75 15.19,5.55 14.59,4.44C16.43,5.07 17.96,6.34 18.92,8M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /> + </svg> + ), + link: x => x, + }, + }; + return ( + <div + className={`py-8 sm:py-12 lg:py-8 px-4 sm:flex sm:flex-col xl:py-16 sm:px-8 border border-blue-900`} + > + <blockquote className="sm:flex-grow sm:flex sm:items-center text-center sm:text-left max-w-sm sm:max-w-3xl mx-auto w-full"> + <div className="flex-shrink-0"> + <div className={`inline-flex rounded-full border-2 border-white`}> + <div className="w-36 h-36 md:h-48 md:w-48 lg:h-36 lg:w-36 xl:h-48 xl:w-48"> + <Img + className="rounded-full" + fixed={gatsbyImage.fixed} + alt={author.name} + style={{ width: '100%', height: '100%' }} + /> + </div> + </div> + </div> + <div className="mt-4 sm:mt-0 sm:ml-8 lg:ml-6 xl:ml-8"> + <div className="space-x-2"> + <span className="inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-blue-700 text-blue-100"> + {author.title} + </span> + </div> + <div className="text-3xl font-bold text-white">{author.name}</div> + <div className="mt-4 text-base text-blue-200"> + <p className="relative">{author.blurb}</p> + </div> + <div className="mt-2 flex space-x-2 justify-center sm:justify-start"> + {Object.keys(socialMedia) + .filter(sm => author.hasOwnProperty(sm)) + .map(sm => ( + <a + key={author.name + sm} + href={socialMedia[sm].link(author[sm])} + className="text-blue-300 hover:text-blue-200 transition duration-100" + > + <span className="sr-only">{sm}</span> + {socialMedia[sm].icon} + </a> + ))} + </div> + </div> + </blockquote> + </div> + ); +}; export default function IndexPage(props: PageProps) { return ( @@ -73,7 +195,7 @@ export default function IndexPage(props: PageProps) { <div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-start"> <div className="rounded-md shadow"> <Link - to="/intro" + to="/dashboard/" className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-blue-600 hover:bg-blue-500 focus:outline-none focus:border-blue-700 focus:shadow-outline-blue transition duration-150 ease-in-out md:py-4 md:text-lg md:px-10" > View Guide @@ -181,35 +303,25 @@ export default function IndexPage(props: PageProps) { for USACO contestants – available to everyone, for free. </p> - <div className="max-w-3xl mx-auto bg-yellow-50 rounded-md border-yellow-400 border p-4 mt-8"> - <div className="flex"> - <div className="flex-shrink-0"> - <svg - className="h-5 w-5 text-yellow-400" - viewBox="0 0 20 20" - fill="currentColor" - > - <path - fillRule="evenodd" - d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" - clipRule="evenodd" - /> - </svg> - </div> - <div className="ml-3 flex-1 md:flex md:justify-between"> - <p className="text-sm leading-5 text-yellow-700 text-left"> - This guide is not a syllabus. Topics on this guide reflect{' '} - <i>past</i> problems, not future problems. - </p> - <p className="mt-3 text-sm leading-5 md:mt-0 md:ml-6"> - <a - href="#" - className="whitespace-no-wrap font-medium text-yellow-700 hover:text-yellow-600 transition ease-in-out duration-150" - > - Learn More → - </a> - </p> - </div> + <div className="inline-flex mx-auto bg-yellow-50 rounded-md p-4 mt-8"> + <div className="flex-shrink-0"> + <svg + className="h-5 w-5 text-yellow-400" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fillRule="evenodd" + d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="ml-3"> + <p className="text-sm leading-5 text-yellow-700 text-left"> + This guide is not an official syllabus. Topics on this guide + reflect <i>past</i> problems, not future problems. + </p> </div> </div> </div> @@ -333,13 +445,22 @@ export default function IndexPage(props: PageProps) { </div> <div className="ml-4"> <h4 className="text-lg leading-6 font-medium text-gray-900"> - Officially Recognized + Stay Motivated </h4> <p className="mt-2 text-base leading-6 text-gray-500"> - This guide is developed in collaboration with USACO Staff - and USACO Director Dr. Brian Dean. + Use our progress-tracking tools to track your progress in + the Guide and stay motivated. </p> </div> + {/*<div className="ml-4">*/} + {/* <h4 className="text-lg leading-6 font-medium text-gray-900">*/} + {/* Officially Recognized*/} + {/* </h4>*/} + {/* <p className="mt-2 text-base leading-6 text-gray-500">*/} + {/* This guide is developed in collaboration with USACO Staff*/} + {/* and USACO Director Dr. Brian Dean.*/} + {/* </p>*/} + {/*</div>*/} </div> </li> </ul> @@ -365,7 +486,7 @@ export default function IndexPage(props: PageProps) { <div className="mt-8 flex justify-center"> <div className="rounded-md shadow"> <Link - to="/intro" + to="/dashboard/" className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-blue-600 hover:bg-blue-500 focus:outline-none focus:border-blue-700 focus:shadow-outline-blue transition duration-150 ease-in-out md:py-4 md:text-lg md:px-10" > View Guide @@ -386,15 +507,14 @@ export default function IndexPage(props: PageProps) { <div> <div> <dt className="text-lg leading-6 font-medium text-gray-900"> - Is this a Syllabus? + Is this an official syllabus? </dt> <dd className="mt-2"> <p className="text-base leading-6 text-gray-500"> - <b>No. This guide is NOT a syllabus.</b> USACO does not - have an official syllabus. This guide merely lists topics - that have <i>historically</i> appeared in USACO contests; - it makes no guarantees about the topics in future USACO - contests. + <b>No, USACO does not have an official syllabus.</b> + This guide merely lists topics that have{' '} + <i>historically</i> appeared in USACO contests; it makes + no guarantees about the topics in future USACO contests. </p> </dd> </div> @@ -406,8 +526,8 @@ export default function IndexPage(props: PageProps) { <p className="text-base leading-6 text-gray-500"> If you encounter an issue while using the guide (website bug, typo, broken link, unclear explanation, etc), use the - "Report an Issue" button on the bottom left of the screen, - or contact the guide coordinator directly. + "Contact Us" button on the bottom left of the screen, or + contact the guide coordinator directly. </p> </dd> </div> @@ -427,15 +547,13 @@ export default function IndexPage(props: PageProps) { <div className="mt-12 md:mt-0"> <div> <dt className="text-lg leading-6 font-medium text-gray-900"> - Is this guide for Platinum contestants? + Is this guide for non-USACO competitive programmers? </dt> <dd className="mt-2"> <p className="text-base leading-6 text-gray-500"> - While we offer many resources targeted towards platinum - contestants, the platinum division has too many topics for - us to cover everything. For platinum contestants, we - recommend you treat this as more of a <i>resource</i> than - a <i>guide</i>. + Of course! While we structure our content around USACO, + feel free to use this guide even if you don't do USACO. + You will still learn a lot from it! </p> </dd> </div> @@ -446,8 +564,8 @@ export default function IndexPage(props: PageProps) { <dd className="mt-2"> <p className="text-base leading-6 text-gray-500"> If you get stuck, feel free to ask for help by clicking - the "Get Help" button on the bottom left of the screen. It - may take some time for your question to be addressed. + the "Contact Us" button on the bottom left of the screen. + It may take some time for your question to be addressed. </p> </dd> </div> @@ -476,8 +594,29 @@ export default function IndexPage(props: PageProps) { </div> {/*End FAQ*/} + <section className="bg-blue-800 overflow-hidden"> + <div className="py-6 sm:py-12 text-center border-b-2 border-blue-900"> + <h2 className="text-3xl sm:text-5xl font-extrabold text-white px-4"> + Authors you can trust. + </h2> + </div> + <div className="lg:grid lg:grid-cols-2 -m-px"> + {Authors.map(author => ( + <AuthorCard + author={author} + key={author.name} + gatsbyImage={ + (props.data as any).allFile.edges.find( + x => x.node.name === author.photo + ).node.childImageSharp + } + /> + ))} + </div> + </section> + <div className="bg-white"> - <div className="max-w-screen-xl mx-auto pb-12 px-4"> + <div className="max-w-screen-xl mx-auto py-12 px-4"> <p className="text-center text-base leading-6 text-gray-400"> No part of this website may be reproduced or commercialized in any manner without prior written permission.{' '} @@ -490,3 +629,20 @@ export default function IndexPage(props: PageProps) { </Layout> ); } + +export const query = graphql` + query { + allFile(filter: { relativePath: { regex: "/^authors/images/.*/" } }) { + edges { + node { + childImageSharp { + fixed(width: 192, height: 192, cropFocus: CENTER, quality: 100) { + ...GatsbyImageSharpFixed + } + } + name + } + } + } + } +`; diff --git a/src/pages/license.tsx b/src/pages/license.tsx index b83e090..c2fe6f6 100644 --- a/src/pages/license.tsx +++ b/src/pages/license.tsx @@ -94,9 +94,10 @@ export default function LicensePage(props: PageProps) { > nathan.r.wang@gmail.com </a> - . While USACO Director Dr. Brian Dean will make all final decisions, - please do not contact him directly. Instead, direct any questions - related to the USACO Guide to the coordinator, Nathan Wang. + . + {/*While USACO Director Dr. Brian Dean will make all final decisions,*/} + {/*please do not contact him directly. Instead, direct any questions*/} + {/*related to the USACO Guide to the coordinator, Nathan Wang.*/} </p> <Link to="/" className="block mb-4 underline text-blue-600"> ← Back to Home diff --git a/src/styles/anchor.css b/src/styles/anchor.css new file mode 100644 index 0000000..8109267 --- /dev/null +++ b/src/styles/anchor.css @@ -0,0 +1,31 @@ +.anchor.before { + top: -2px; +} + +h1 .anchor svg, +h2 .anchor svg, +h3 .anchor svg, +h4 .anchor svg, +h5 .anchor svg, +h6 .anchor svg { + visibility: visible; + opacity: 0; + height: 0.875em !important; + width: 0.875em !important; + @apply transition duration-150 ease-in-out; +} + +h1:hover .anchor svg, +h2:hover .anchor svg, +h3:hover .anchor svg, +h4:hover .anchor svg, +h5:hover .anchor svg, +h6:hover .anchor svg, +h1 .anchor:focus svg, +h2 .anchor:focus svg, +h3 .anchor:focus svg, +h4 .anchor:focus svg, +h5 .anchor:focus svg, +h6 .anchor:focus svg { + opacity: 1; +} diff --git a/src/styles/main.css b/src/styles/main.css index 8be576a..9edd5bd 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -1,5 +1,215 @@ /*! purgecss start ignore */ @tailwind base; @tailwind components; + +/* Additional vertical padding used by kbd tag. */ +.py-05 { + padding-top: 0.125rem; + padding-bottom: 0.125rem; +} + +.markdown { + @apply text-gray-900 leading-normal break-words; +} + +.markdown pre { + font-size: 0.875rem; +} + +.markdown > * + * { + @apply mt-0 mb-4; +} + +.markdown li > p + p { + @apply mt-6; +} + +.markdown a { + @apply text-blue-600 font-semibold; +} + +.markdown strong a, +.markdown a strong { + @apply font-bold; +} + +.markdown h5 { + @apply leading-tight text-sm font-semibold mb-4 mt-6; +} + +.markdown h6 { + @apply leading-tight text-sm font-semibold text-gray-600 mb-4 mt-6; +} + +.markdown blockquote { + @apply text-base border-l-4 border-gray-300 pl-4 pr-4 text-gray-600; +} + +.markdown code.inline { + @apply text-sm; +} + +.markdown h1 code.inline { + @apply text-3xl; +} + +.markdown h2 code.inline { + @apply text-2xl; +} + +.markdown h3 code.inline { + @apply text-xl; +} + +.markdown h4 code.inline { + @apply text-lg; +} + +.markdown ul { + @apply pl-8 list-disc mb-6; +} +.markdown ol { + @apply pl-4 mb-6; +} + +.markdown ul ul { + list-style-type: circle; + @apply mt-1 mb-0; +} + +.markdown ul ul ul { + list-style-type: square; +} + +.markdown ul > li { + @apply mb-2; +} + +.markdown ul > li:last-of-type { + @apply mb-0; +} + +.markdown ul ul > li { + @apply mb-1; +} + +.markdown ol > li { + @apply mb-2; +} + +.markdown ol > li:last-of-type { + @apply mb-0; +} + +.markdown ol ol > li { + @apply mb-1; +} + +.markdown ul p { + @apply mb-0; +} + +.markdown ol { + counter-reset: number; +} + +.markdown ol li { + display: flex; + counter-increment: number; +} + +.markdown ol li:before { + content: counters(number, '.') '.'; + @apply text-blue-600 font-semibold mr-2; +} + +.markdown kbd { + @apply text-xs inline-block rounded border px-1 py-05 align-middle font-normal font-mono shadow; +} + +/* Override pygments style background color. */ +.markdown .highlight pre { + @apply bg-gray-100 !important; +} + +.spoiler-body > pre { + margin-left: -1rem !important; + margin-right: -1rem !important; + margin-bottom: -1rem !important; +} +.spoiler-body > pre + p { + @apply mt-4; +} + +.tailwind-alert p + ul { + @apply -mt-2; +} +.tailwind-alert ul { + @apply mb-2; +} +.tailwind-alert ul li { + @apply mb-1; +} +.no-bottom-margin p:last-child { + @apply mb-0; +} +.tailwind-alert a { + @apply text-black underline font-normal; +} + +.table-alternating-stripes tr:nth-child(even) { + @apply bg-gray-50; +} + +/* from https://www.lekoarts.de/en/blog/language-tabs-for-gatsbys-code-blocks */ +.gatsby-highlight { + position: relative; + -webkit-overflow-scrolling: touch; +} +.gatsby-highlight pre[class*='language-'] { + -webkit-overflow-scrolling: touch; +} +.gatsby-highlight pre[class*='language-']::before { + background: black; + border-radius: 0 0 0.25rem 0.25rem; + color: white; + font-size: 12px; + letter-spacing: 0.025rem; + padding: 0.1rem 0.5rem; + position: absolute; + right: 1rem; + text-align: right; + text-transform: uppercase; + top: 0; +} +.gatsby-highlight pre[class*='language-cpp']::before { + content: 'cpp'; + background: #f7df1e; + color: black; +} +.gatsby-highlight pre[class*='language-java']::before { + content: 'java'; + background: #ff9800; + color: black; +} +.gatsby-highlight pre[class*='language-py']::before { + content: 'python'; + background: #ff3a5b; + color: black; +} + +.yt-video-container { + position: relative; + padding-bottom: 56.25%; /* 16:9 */ + height: 0; +} +.yt-video-container iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} /*! purgecss end ignore */ + @tailwind utilities; diff --git a/src/styles/markdown.css b/src/styles/markdown.css deleted file mode 100644 index ec50e8b..0000000 --- a/src/styles/markdown.css +++ /dev/null @@ -1,124 +0,0 @@ -/*! purgecss start ignore */ -/* Additional vertical padding used by kbd tag. */ -.py-05 { - padding-top: 0.125rem; - padding-bottom: 0.125rem; -} - -.markdown { - @apply text-gray-900 leading-normal break-words; -} - -.markdown pre { - font-size: 0.875rem; -} - -.markdown > * + * { - @apply mt-0 mb-4; -} - -.markdown li + li { - @apply mt-1; -} - -.markdown li > p + p { - @apply mt-6; -} - -.markdown strong { - @apply font-semibold; -} - -.markdown a { - @apply text-blue-600 font-semibold; -} - -.markdown strong a { - @apply font-bold; -} - -.markdown h1 { - @apply leading-tight border-b text-4xl font-semibold mb-4 mt-6 pb-2; -} - -.markdown h2 { - @apply leading-tight border-b text-2xl font-semibold mb-4 mt-6 pb-2; -} - -.markdown h4 { - @apply leading-none text-base font-semibold mb-4 mt-6; -} - -.markdown h5 { - @apply leading-tight text-sm font-semibold mb-4 mt-6; -} - -.markdown h6 { - @apply leading-tight text-sm font-semibold text-gray-600 mb-4 mt-6; -} - -.markdown blockquote { - @apply text-base border-l-4 border-gray-300 pl-4 pr-4 text-gray-600; -} - -.markdown ul { - @apply text-base list-disc mb-4; -} - -.markdown ul ul { - list-style-type: circle; -} - -.markdown ul ul ul { - list-style-type: square; -} - -.markdown--module ul { - @apply pl-8; -} - -.markdown ul ul { - @apply mb-0; -} - -.markdown ul p { - @apply mb-0; -} - -.markdown ol { - @apply text-base pl-8 list-decimal mb-4; -} - -.markdown kbd { - @apply text-xs inline-block rounded border px-1 py-05 align-middle font-normal font-mono shadow; -} - -/* Override pygments style background color. */ -.markdown .highlight pre { - @apply bg-gray-100 !important; -} - -.markdown--module .syllabus-only { - display: none; -} - -.spoiler-body > pre { - margin-left: -1rem !important; - margin-right: -1rem !important; - margin-bottom: -1rem !important; -} - -.info-block .info-block__heading { - @apply text-sm leading-5 font-medium text-blue-800 my-0; -} -.info-block .info-block__body { - @apply text-sm leading-5 text-blue-700 mt-2; -} -.info-block .info-block__body p:last-child { - @apply mb-0; -} - -.table-alternating-stripes tr:nth-child(even) { - @apply bg-gray-50; -} -/*! purgecss end ignore */ diff --git a/src/templates/moduleTemplate.tsx b/src/templates/moduleTemplate.tsx index 9638827..33a2c92 100644 --- a/src/templates/moduleTemplate.tsx +++ b/src/templates/moduleTemplate.tsx @@ -1,591 +1,36 @@ import * as React from 'react'; -import { useState } from 'react'; -import { graphql, Link } from 'gatsby'; +import { graphql } from 'gatsby'; import Layout from '../components/layout'; import Markdown from '../components/markdown/Markdown'; -import ModuleOrdering, { - divisionLabels, - isModuleOrderingGroup, - ModuleOrderingGroup, - ModuleOrderingItem, -} from '../../content/ordering'; -import Transition from '../components/Transition'; -import { graphqlToModuleInfo, graphqlToModuleLinks } from '../utils'; +import { SECTION_LABELS } from '../../content/ordering'; +import { graphqlToModuleInfo } from '../utils/utils'; import SEO from '../components/seo'; - -// @ts-ignore -import logo from '../assets/logo.svg'; -import { ModuleFrequency, ModuleInfo, ModuleLinkInfo } from '../module'; - -const renderPrerequisite = prerequisite => { - return <li key={prerequisite}>{prerequisite}</li>; -}; - -const Breadcrumbs = ({ - division, - module, -}: { - division: string; - module: ModuleLinkInfo; -}) => ( - <nav className="flex items-center text-sm leading-5 font-medium"> - <Link - to="/" - className="text-gray-500 hover:text-gray-700 transition duration-150 ease-in-out" - > - Home - </Link> - <svg - className="flex-shrink-0 mx-2 h-5 w-5 text-gray-400" - viewBox="0 0 20 20" - fill="currentColor" - > - <path - fillRule="evenodd" - d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" - clipRule="evenodd" - /> - </svg> - <Link - to={`/${division}`} - className="text-gray-500 hover:text-gray-700 transition duration-150 ease-in-out" - > - {divisionLabels[division]} - </Link> - <svg - className="flex-shrink-0 mx-2 h-5 w-5 text-gray-400" - viewBox="0 0 20 20" - fill="currentColor" - > - <path - fillRule="evenodd" - d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" - clipRule="evenodd" - /> - </svg> - <span className="text-gray-500">{module.title}</span> - </nav> -); - -const SidebarBottomButtons = () => ( - <> - <div className="flex-shrink-0 border-t border-gray-200 flex"> - <button className="group flex-1 flex items-center p-4 text-sm leading-5 font-medium text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-100 transition ease-in-out duration-150"> - <svg - className="mr-4 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" - fill="none" - viewBox="0 0 24 24" - stroke="currentColor" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="2" - > - <path d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> - </svg> - Report an Issue - </button> - </div> - <div className="flex-shrink-0 border-t border-gray-200 flex"> - <button className="group flex-1 flex items-center p-4 text-sm leading-5 font-medium text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-100 transition ease-in-out duration-150"> - <svg - className="mr-4 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" - fill="none" - viewBox="0 0 24 24" - stroke="currentColor" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="2" - > - <path d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" /> - </svg> - Get Help - </button> - </div> - </> -); - -interface NavLinkGroup { - label: string; - children: NavLinkItem[]; -} - -type NavLinkItem = ModuleLinkInfo | NavLinkGroup; - -const isNavLinkGroup = (x: NavLinkItem): x is NavLinkGroup => - x.hasOwnProperty('label'); - -const SidebarNavLinks = ({ links }: { links: NavLinkItem[] }) => { - const renderLink = (link: NavLinkItem) => { - if (link instanceof ModuleLinkInfo) { - return ( - <Link - to={link.url} - className={`flex items-center px-6 py-3 text-sm leading-5 text-gray-600 hover:text-gray-900 hover:bg-gray-100 focus:outline-none focus:bg-gray-200 transition ease-in-out duration-150`} - > - {link.title} - </Link> - ); - } - return ( - <div className="bg-gray-100 mb-4"> - <div - className={`flex items-center px-6 py-3 text-sm leading-5 font-medium text-gray-600 border-b border-gray-200`} - > - {link.label} - </div> - {link.children.map(renderLink)} - </div> - ); - }; - return <>{links.map(renderLink)}</>; -}; - -const TopNav = ({ - division, - module, - prevModule, - nextModule, -}: { - division: any; - module: ModuleLinkInfo; - prevModule: ModuleLinkInfo | null; - nextModule: ModuleLinkInfo | null; -}) => { - const disabledClasses = 'text-gray-200 pointer-events-none'; - const activeClasses = - 'text-gray-500 hover:text-gray-800 transition duration-150 ease-in-out'; - return ( - <div className="flex justify-between"> - <span className="-ml-4 rounded-md"> - <Link - to={prevModule === null ? module.url : prevModule.url} - className={ - 'inline-flex items-center px-4 py-2 text-sm leading-5 font-medium rounded-md ' + - (prevModule === null ? disabledClasses : activeClasses) - } - > - <svg - className="-ml-0.5 mr-1 h-4 w-4" - fill="none" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="2" - viewBox="0 0 24 24" - stroke="currentColor" - > - <path d="M15 19l-7-7 7-7" /> - </svg> - Prev - </Link> - </span> - <div className="hidden sm:flex items-center"> - <Breadcrumbs division={division} module={module} /> - </div> - <span className="rounded-md -mr-4"> - <Link - to={nextModule === null ? module.url : nextModule.url} - className={ - 'inline-flex items-center px-4 py-2 text-sm leading-5 font-medium rounded-md ' + - (nextModule === null ? disabledClasses : activeClasses) - } - > - Next - <svg - className="-mr-0.5 ml-1 h-4 w-4" - fill="none" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="2" - viewBox="0 0 24 24" - stroke="currentColor" - > - <path d="M9 5l7 7-7 7" /> - </svg> - </Link> - </span> - </div> - ); -}; - -const CompactNav = ({ - division, - module, - prevModule, - nextModule, -}: { - division: string; - module: ModuleLinkInfo; - prevModule: ModuleLinkInfo | null; - nextModule: ModuleLinkInfo | null; -}) => ( - <div className="flex"> - <div className="hidden sm:flex items-center"> - <Breadcrumbs division={division} module={module} /> - </div> - <div className="flex-1 flex items-center justify-between sm:justify-end"> - <span className="rounded-md"> - {prevModule && ( - <Link - to={prevModule.url} - className="inline-flex items-center px-3 py-2 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50 transition ease-in-out duration-150" - > - <svg - className="-ml-0.5 mr-1 h-4 w-4" - fill="none" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="2" - viewBox="0 0 24 24" - stroke="currentColor" - > - <path d="M15 19l-7-7 7-7" /> - </svg> - Prev - </Link> - )} - </span> - <span className="ml-3 rounded-md"> - <button - type="button" - className="inline-flex items-center px-3 py-2 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50 transition ease-in-out duration-150" - > - Mark Complete - </button> - </span> - <span className="ml-3 rounded-md"> - {nextModule && ( - <Link - to={nextModule.url} - className="inline-flex items-center px-3 py-2 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50 transition ease-in-out duration-150" - > - Next - <svg - className="-mr-0.5 ml-1 h-4 w-4" - fill="none" - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth="2" - viewBox="0 0 24 24" - stroke="currentColor" - > - <path d="M9 5l7 7-7 7" /> - </svg> - </Link> - )} - </span> - </div> - </div> -); - -const flattenNavLinks = (navLinks: NavLinkItem[]) => { - let links: ModuleLinkInfo[] = []; - const flatten = (link: NavLinkItem) => { - if (isNavLinkGroup(link)) { - link.children.forEach(flatten); - } else { - links.push(link); - } - }; - navLinks.forEach(flatten); - return links; -}; - -const Frequency = ({ frequency }: { frequency: ModuleFrequency }) => { - const textColors = [ - 'text-green-600', - 'text-red-600', - 'text-orange-600', - 'text-yellow-600', - 'text-teal-600', - ]; - const circleColors = [ - 'text-green-500', - 'text-red-500', - 'text-orange-500', - 'text-yellow-500', - 'text-teal-500', - ]; - const labels = [ - 'Has Not Appeared', - 'Rare (1-2 times)', - 'Not Frequent (3-4 times)', - 'Somewhat Frequent', - 'Very Frequent (historically ~ once per contest)', - ]; - const emptyCircle = 'text-gray-300'; - - return ( - <span - className={`inline-flex items-center font-medium ${textColors[frequency]}`} - > - {new Array(4).fill(null).map((_, idx) => ( - <svg - className={`-ml-1 mr-1.5 h-2.5 w-2.5 ${ - idx >= frequency ? emptyCircle : circleColors[frequency] - }`} - fill="currentColor" - viewBox="0 0 8 8" - > - <circle cx="4" cy="4" r="3" /> - </svg> - ))} - {labels[frequency]} - </span> - ); -}; +import ModuleLayout from '../components/ModuleLayout/ModuleLayout'; +import { useContext } from 'react'; +import UserDataContext from '../context/UserDataContext'; export default function Template(props) { - const { mdx, allMdx } = props.data; // data.markdownRemark holds your post data + const { mdx } = props.data; // data.markdownRemark holds your post data const { body } = mdx; - const prereqs = mdx.frontmatter.prerequisites; - const division = props.pageContext.division; - - const [isMobileNavOpen, setIsMobileNavOpen] = useState(false); - const navLinks: NavLinkItem[] = React.useMemo(() => { - const modules = graphqlToModuleLinks(allMdx); - const getLinks = (item: ModuleOrderingItem): NavLinkItem => { - if (isModuleOrderingGroup(item)) { - return { - label: item.name, - children: item.items.map(getLinks), - }; - } - if (!modules.hasOwnProperty(item)) { - throw `${item} not found!`; - } - return modules[item]; - }; - return ModuleOrdering[division].map(getLinks); + const module = React.useMemo(() => graphqlToModuleInfo(mdx), [mdx]); + const { setLastViewedModule } = useContext(UserDataContext); + React.useEffect(() => { + setLastViewedModule(module.id); }, []); - - const module = graphqlToModuleInfo(mdx); - const prevModule: ModuleLinkInfo | null = React.useMemo(() => { - const links = flattenNavLinks(navLinks); - for (let i = 0; i < links.length - 1; i++) { - if (links[i + 1].id === module.id) return links[i]; - } - return null; - }, [navLinks]); - const nextModule: ModuleLinkInfo | null = React.useMemo(() => { - const links = flattenNavLinks(navLinks); - for (let i = 1; i < links.length; i++) { - if (links[i - 1].id === module.id) return links[i]; - } - return null; - }, [navLinks]); - return ( <Layout> - <SEO title={`${module.title} (${divisionLabels[division]})`} /> + <SEO + title={`${module.title} (${SECTION_LABELS[module.section]})`} + description={module.description} + /> <div className="h-screen flex overflow-hidden bg-white"> - {/* Off-canvas menu for mobile */} - <Transition show={isMobileNavOpen}> - <div className="lg:hidden"> - <div className="fixed inset-0 flex z-40"> - <Transition - enter="transition-opacity ease-linear duration-300" - enterFrom="opacity-0" - enterTo="opacity-100" - leave="transition-opacity ease-linear duration-300" - leaveFrom="opacity-100" - leaveTo="opacity-0" - > - <div - className="fixed inset-0" - onClick={() => setIsMobileNavOpen(false)} - > - <div className="absolute inset-0 bg-gray-600 opacity-75" /> - </div> - </Transition> - - <Transition - enter="transition ease-in-out duration-300 transform" - enterFrom="-translate-x-full" - enterTo="translate-x-0" - leave="transition ease-in-out duration-300 transform" - leaveFrom="translate-x-0" - leaveTo="-translate-x-full" - > - <div className="relative flex-1 flex flex-col max-w-xs w-full bg-white"> - <div className="absolute top-0 right-0 -mr-14 p-1"> - <button - className="flex items-center justify-center h-12 w-12 rounded-full focus:outline-none focus:bg-gray-600" - aria-label="Close sidebar" - onClick={() => setIsMobileNavOpen(false)} - > - <svg - className="h-6 w-6 text-white" - stroke="currentColor" - fill="none" - viewBox="0 0 24 24" - > - <path - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth={2} - d="M6 18L18 6M6 6l12 12" - /> - </svg> - </button> - </div> - <div className="flex-1 h-0 pt-5 pb-4 overflow-y-auto"> - <div className="flex-shrink-0 flex items-center px-4"> - <img className="h-12 w-auto" src={logo} alt="Workflow" /> - </div> - <div className="mt-4 px-6"> - <Breadcrumbs division={division} module={module} /> - </div> - <nav className="mt-2"> - <SidebarNavLinks links={navLinks} /> - </nav> - </div> - <SidebarBottomButtons /> - </div> - </Transition> - <div className="flex-shrink-0 w-14"> - {/* Force sidebar to shrink to fit close icon */} - </div> - </div> - </div> - </Transition> - {/* Static sidebar for desktop */} - <div className="hidden lg:flex lg:flex-shrink-0"> - <div className="flex flex-col w-64 border-r border-gray-200 bg-white"> - <div className="h-0 flex-1 flex flex-col pt-5 pb-4 overflow-y-auto"> - <div className="flex items-center flex-shrink-0 px-4"> - <img className="h-12 w-auto" src={logo} alt="Workflow" /> - </div> - {/* Sidebar component, swap this element with another sidebar if you like */} - <nav className="mt-2 flex-1 bg-white"> - <SidebarNavLinks links={navLinks} /> - </nav> - </div> - <SidebarBottomButtons /> - </div> - </div> - <div className="flex flex-col w-0 flex-1 overflow-hidden"> - <div className="lg:hidden pl-1 pt-1 sm:pl-3 sm:pt-3"> - <button - className="-ml-0.5 -mt-0.5 h-12 w-12 inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900 focus:outline-none focus:bg-gray-200 transition ease-in-out duration-150" - aria-label="Open sidebar" - onClick={() => setIsMobileNavOpen(true)} - > - <svg - className="h-6 w-6" - fill="none" - viewBox="0 0 24 24" - stroke="currentColor" - > - <path - strokeLinecap="round" - strokeLinejoin="round" - strokeWidth={2} - d="M4 6h16M4 12h16M4 18h16" - /> - </svg> - </button> + <ModuleLayout module={module}> + <div className="py-4"> + <Markdown body={body} /> </div> - <main - className="flex-1 relative z-0 overflow-y-auto sm:pt-2 pb-6 focus:outline-none" - tabIndex={0} - > - <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8"> - <div> - <div className="hidden lg:block"> - <TopNav - division={division} - module={module} - prevModule={prevModule} - nextModule={nextModule} - /> - </div> - <div className="lg:hidden mb-6"> - <CompactNav - division={division} - module={module} - prevModule={prevModule} - nextModule={nextModule} - /> - </div> - <div className="px-1.5 lg:mt-8"> - {module.frequency && ( - <Frequency frequency={module.frequency} /> - )} - </div> - <div className="sm:flex sm:items-center sm:justify-between"> - <div className="flex-1 min-w-0"> - <h1 className="text-2xl font-bold text-gray-900 sm:text-3xl"> - {module.title} - </h1> - <p className={`text-gray-500`}>Author: {module.author}</p> - </div> - <div className="hidden lg:flex-shrink-0 lg:flex ml-4"> - <span className="shadow-sm rounded-md"> - <button - type="button" - className="inline-flex items-center px-4 py-2 border border-gray-300 text-sm leading-5 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:text-gray-800 active:bg-gray-50 transition duration-150 ease-in-out" - > - Mark Complete - </button> - </span> - </div> - </div> - </div> - <div className="py-4"> - {prereqs && ( - <div className="rounded-md bg-blue-50 p-4 mb-6"> - <div className="flex"> - <div className="flex-shrink-0"> - <svg - className="h-5 w-5 text-blue-400" - fill="currentColor" - viewBox="0 0 20 20" - > - <path - fillRule="evenodd" - d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" - clipRule="evenodd" - /> - </svg> - </div> - <div className="ml-3"> - <h3 className="text-sm leading-5 font-medium text-blue-800"> - Prerequisites - </h3> - <div className="mt-2 text-sm leading-5 text-blue-800"> - <ul className="list-disc list-inside pl-3 space-y-1"> - {prereqs.map(renderPrerequisite)} - </ul> - </div> - </div> - </div> - </div> - )} - - {module.description && ( - <div className="rounded-md bg-green-50 p-4 border border-green-500 mb-12"> - <p className="text-sm leading-5 font-medium text-green-800"> - {module.description} - </p> - </div> - )} - - <Markdown body={body} className="markdown--module" /> - </div> - - <div className="border-t border-gray-200 pt-4"> - <CompactNav - division={division} - module={module} - prevModule={prevModule} - nextModule={nextModule} - /> - </div> - </div> - </main> - </div> + </ModuleLayout> </div> </Layout> ); @@ -602,14 +47,24 @@ export const pageQuery = graphql` description frequency } - } - allMdx { - edges { - node { - frontmatter { - title - id - } + fields { + division + } + toc { + cpp { + depth + value + slug + } + java { + depth + value + slug + } + py { + depth + value + slug } } } diff --git a/src/templates/syllabusTemplate.tsx b/src/templates/syllabusTemplate.tsx index 74524c1..5d93ad6 100644 --- a/src/templates/syllabusTemplate.tsx +++ b/src/templates/syllabusTemplate.tsx @@ -2,34 +2,49 @@ import * as React from 'react'; import Layout from '../components/layout'; import SEO from '../components/seo'; -import { graphql, Link } from 'gatsby'; -import { divisionLabels, divisions } from '../../content/ordering'; -import SyllabusModule from '../components/SyllabusModule'; -import { getModule } from '../utils'; +import { graphql } from 'gatsby'; +import MODULE_ORDERING, { + moduleIDToSectionMap, + SECTION_LABELS, +} from '../../content/ordering'; +import { getModule } from '../utils/utils'; +import TopNavigationBar from '../components/TopNavigationBar'; +import DashboardProgress from '../components/Dashboard/DashboardProgress'; +import UserDataContext from '../context/UserDataContext'; +import getProgressInfo from '../utils/getProgressInfo'; +import ModuleLink from '../components/Dashboard/ModuleLink'; +import { ModuleLinkInfo } from '../models/module'; +import styled from 'styled-components'; +import tw from 'twin.macro'; -const renderModule = (node, idx, parentIdx = -1) => { - if (node.hasOwnProperty('items')) { - return node.items.map((x, i) => renderModule(x, i, idx)); +const DottedLineContainer = styled.div` + ${tw`space-y-6 relative`} + + @media (min-width: 768px) { + &::before { + content: ''; + position: absolute; + width: 2px; + display: block; + left: calc(50% - 1px); + top: 0; + bottom: 0; + border-right: 2px dashed; + ${tw`border-gray-100`} + } } +`; - const data = node.frontmatter; - if (!data.title) return; +const SectionContainer = styled.div` + ${tw`flex flex-col md:flex-row`} - return ( - <SyllabusModule - title={`${parentIdx !== -1 ? parentIdx + 1 + '.' : ''}${idx + 1}. ${ - data.title - }`} - url={node.slug} - key={node.slug} - problems={data.problems} - prerequisites={data.prerequisites} - author={data.author} - > - {data.description} - </SyllabusModule> - ); -}; + &:hover h2 { + ${tw`text-gray-600`} + } + &:hover h2 + p { + ${tw`text-gray-500`} + } +`; export default function Template(props) { const data = props.data; @@ -39,242 +54,113 @@ export default function Template(props) { return acc; }, {}); - const [selectedDivision, setSelectedDivision] = React.useState( - props.pageContext.division - ); - const colors = { - intro: 'blue', - general: 'pink', - bronze: 'orange', - silver: 'teal', - gold: 'yellow', - plat: 'purple', - adv: 'green', - }; - const color = colors[selectedDivision]; - const module = getModule(allModules, selectedDivision); - - // for PurgeCSS, we have to list all the classes that are dynamically generated... - /* - bg-blue-500 hover:bg-blue-50 hover:text-blue-600 focus:shadow-outline-blue - bg-pink-500 hover:bg-pink-50 hover:text-pink-600 focus:shadow-outline-pink - bg-orange-500 hover:bg-orange-50 hover:text-orange-600 focus:shadow-outline-orange - bg-teal-500 hover:bg-teal-50 hover:text-teal-600 focus:shadow-outline-teal - bg-yellow-500 hover:bg-yellow-50 hover:text-yellow-600 focus:shadow-outline-yellow - bg-purple-500 hover:bg-purple-50 hover:text-purple-600 focus:shadow-outline-purple - bg-green-500 hover:bg-green-50 hover:text-green-600 focus:shadow-outline-green - border-blue-500 text-blue-600 focus:text-blue-800 focus:border-blue-700 border-blue-300 focus:border-blue-300 text-blue-500 text-blue-300 bg-blue-600 - border-pink-500 text-pink-600 focus:text-pink-800 focus:border-pink-700 border-pink-300 focus:border-pink-300 text-pink-500 text-pink-300 bg-pink-600 - border-orange-500 text-orange-600 focus:text-orange-800 focus:border-orange-700 border-orange-300 focus:border-orange-300 text-orange-500 text-orange-300 bg-orange-600 - border-teal-500 text-teal-600 focus:text-teal-800 focus:border-teal-700 border-teal-300 focus:border-teal-300 text-teal-500 text-teal-300 bg-teal-600 - border-yellow-500 text-yellow-600 focus:text-yellow-800 focus:border-yellow-700 border-yellow-300 focus:border-yellow-300 text-yellow-500 text-yellow-300 bg-yellow-600 - border-purple-500 text-purple-600 focus:text-purple-800 focus:border-purple-700 border-purple-300 focus:border-purple-300 text-purple-500 text-purple-300 bg-purple-600 - border-green-500 text-green-600 focus:text-green-800 focus:border-green-700 border-green-300 focus:border-green-300 text-green-500 text-green-300 bg-green-600 - */ - // alternatively we can just not dynamically generate classes, but that seems more tedious. + const { division } = props.pageContext; - const selectedTabClasses = `flex-1 py-4 px-3 text-center border-b-2 border-${color}-500 font-bold text-lg leading-5 text-${color}-600 focus:outline-none focus:text-${color}-800 focus:border-${color}-700`, - unselectedTabClasses = `flex-1 py-4 px-3 text-center border-b-2 border-transparent font-bold text-lg leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300`; + const section = getModule(allModules, division); - const handleDivisionChange = d => { - setSelectedDivision(d); - window.history.pushState(null, '', `/${d}/`); - }; + const { userProgressOnModules, userProgressOnProblems } = React.useContext( + UserDataContext + ); + const moduleIDs = section.reduce( + (acc, cur) => [...acc, ...cur.items.map(x => x.frontmatter.id)], + [] + ); + let moduleProgressInfo = getProgressInfo( + moduleIDs, + userProgressOnModules, + ['Complete'], + ['Reading', 'Practicing'], + ['Skipped'], + ['Not Started'] + ); + let problemIDs = []; + for (let chapter of MODULE_ORDERING[division]) { + for (let moduleID of chapter.items) { + for (let problem of allModules[moduleID].problems) { + problemIDs.push(problem.uniqueID); + } + } + } + const problemsProgressInfo = getProgressInfo( + problemIDs, + userProgressOnProblems, + ['Solved'], + ['Solving'], + ['Skipped'], + ['Not Attempted'] + ); return ( <Layout> - <SEO title={divisionLabels[selectedDivision]} /> + <SEO title={SECTION_LABELS[division]} /> + <div className="min-h-screen"> + <TopNavigationBar /> - <div className="min-h-screen bg-gray-50"> - {/* Begin Hero Section */} - <div - className={`relative bg-${color}-600 overflow-hidden transition duration-300 pb-48`} - > - <div className="hidden sm:block sm:absolute sm:inset-y-0 sm:h-full sm:w-full"> - <div className="relative h-full max-w-screen-xl mx-auto"> - <svg - className="absolute right-full transform translate-y-1/4 translate-x-1/4 lg:translate-x-1/2" - width={404} - height={784} - fill="none" - viewBox="0 0 404 784" - > - <defs> - <pattern - id="f210dbf6-a58d-4871-961e-36d5016a0f49" - x={0} - y={0} - width={20} - height={20} - patternUnits="userSpaceOnUse" - > - <rect - x={0} - y={0} - width={4} - height={4} - className={`text-${color}-500 transition duration-300`} - fill="currentColor" - /> - </pattern> - </defs> - <rect - width={404} - height={784} - fill="url(#f210dbf6-a58d-4871-961e-36d5016a0f49)" - /> - </svg> - <svg - className="absolute left-full transform -translate-y-3/4 -translate-x-1/4 md:-translate-y-1/2 lg:-translate-x-1/2" - width={404} - height={784} - fill="none" - viewBox="0 0 404 784" - > - <defs> - <pattern - id="5d0dd344-b041-4d26-bec4-8d33ea57ec9b" - x={0} - y={0} - width={20} - height={20} - patternUnits="userSpaceOnUse" - > - <rect - x={0} - y={0} - width={4} - height={4} - className={`text-${color}-500 transition duration-300`} - fill="currentColor" - /> - </pattern> - </defs> - <rect - width={404} - height={784} - fill="url(#5d0dd344-b041-4d26-bec4-8d33ea57ec9b)" - /> - </svg> - </div> - </div> - <div className="relative pt-6 pb-12 sm:pb-16 md:pb-20 lg:pb-28 xl:pb-32"> - <div className="mt-10 mx-auto max-w-screen-xl px-4 sm:mt-12 sm:px-6 md:mt-16 lg:mt-20 xl:mt-28"> - <div className="text-center"> - <h2 className="text-4xl tracking-tight leading-10 font-extrabold text-white sm:text-5xl sm:leading-none md:text-6xl"> - USACO Guide - </h2> - <p - className={`mt-3 max-w-md mx-auto text-base text-${color}-300 sm:text-lg md:mt-5 md:text-xl md:max-w-3xl`} - > - A collection of curated, high-quality resources to take you - from Bronze to Platinum. - </p> - <div className="mt-5 max-w-md mx-auto sm:flex sm:justify-center md:mt-8"> - <div className="rounded-md shadow"> - <Link - to="/" - className={`w-full flex items-center justify-center px-8 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-${color}-500 hover:bg-${color}-50 hover:text-${color}-600 focus:outline-none focus:shadow-outline-${color} transition duration-300 ease-in-out md:py-4 md:text-lg md:px-10`} - > - About This Guide - </Link> + <main> + <div className="bg-gray-100 py-12"> + <div className="max-w-7xl mx-auto sm:px-6 lg:px-8"> + <h1 className="mb-8 text-blue-700 text-5xl tracking-tight leading-10 font-extrabold text-white sm:leading-none md:text-6xl text-center"> + {SECTION_LABELS[division]} + </h1> + <div className="grid max-w-2xl mx-auto lg:max-w-full lg:grid-cols-2 gap-8"> + <div className="bg-white shadow sm:rounded-lg"> + <div className="px-4 py-5 sm:p-6"> + <h3 className="text-lg leading-6 font-medium text-gray-900"> + Modules Progress + </h3> + <div className="mt-6"> + <DashboardProgress + {...moduleProgressInfo} + total={moduleIDs.length} + /> + </div> + </div> + </div> + <div className="bg-white shadow sm:rounded-lg"> + <div className="px-4 py-5 sm:p-6"> + <h3 className="text-lg leading-6 font-medium text-gray-900"> + Problems Progress + </h3> + <div className="mt-6"> + <DashboardProgress + {...problemsProgressInfo} + total={problemIDs.length} + /> + </div> </div> </div> </div> </div> </div> - </div> - {/* End Hero Section */} - - <div className="pb-8" id="content"> - <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 pb-4"> - <div - className="mb-8 bg-white shadow-md rounded-lg relative" - style={{ marginTop: '-12rem' }} - > - <div className="sm:hidden"> - <select - aria-label="Selected tab" - className="form-select block w-full" - onChange={e => handleDivisionChange(e.target.value)} - value={selectedDivision} - > - {divisions.map(division => ( - <option key={division} value={division}> - {divisionLabels[division]} - </option> - ))} - </select> - </div> - <div className="hidden sm:block border-b border-gray-200"> - <nav className="flex -mb-px"> - {divisions.map(division => ( - <a - key={division} - href={`/${division}`} - onClick={e => { - e.preventDefault(); - handleDivisionChange(division); - }} - className={ - selectedDivision === division - ? selectedTabClasses - : unselectedTabClasses + <DottedLineContainer className="py-12 px-4"> + {section.map(category => ( + <SectionContainer> + <div className="flex-1 md:text-right pr-12 group"> + <h2 className="text-2xl font-semibold leading-6 py-3 text-gray-500 group-hover:text-gray-800 transition duration-150 ease-in-out"> + {category.name} + </h2> + <p className="md:max-w-sm md:ml-auto text-gray-400 group-hover:text-gray-600 transition duration-150 ease-in-out"> + {category.description} + </p> + </div> + <div className="flex-1 pl-12"> + {category.items.map(item => ( + <ModuleLink + link={ + new ModuleLinkInfo( + item.frontmatter.id, + moduleIDToSectionMap[item.frontmatter.id], + item.frontmatter.title, + item.frontmatter.description + ) } - > - {divisionLabels[division]} - </a> + /> ))} - </nav> - </div> - - <ol className="list-inside py-8 px-8 text-lg space-y-1"> - {module.map((m, idx) => { - if (m.hasOwnProperty('items')) { - return ( - <li key={m.name}> - <span className="inline-block w-4 text-right"> - {idx + 1}.{' '} - </span> - <span className="ml-2">{m.name}</span> - <ol className="list-inside px-6 text-lg space-y-1 mb-2"> - {m.items.map((m, idx2) => ( - <li key={m.slug}> - <span className="inline-block w-8 text-right"> - {idx + 1}.{idx2 + 1}.{' '} - </span> - <Link - className="ml-2 text-blue-600 underline" - to={m.slug} - > - {m.frontmatter.title} - </Link> - </li> - ))} - </ol> - </li> - ); - } - return ( - <li key={m.frontmatter.id}> - <span className="inline-block w-4 text-right"> - {idx + 1}.{' '} - </span> - <Link - className="ml-2 text-blue-600 underline" - to={m.slug} - > - {m.frontmatter.title} - </Link> - </li> - ); - })} - </ol> - </div> - - {module.map((x, idx) => renderModule(x, idx))} - </div> - </div> + </div> + </SectionContainer> + ))} + </DottedLineContainer> + </main> </div> </Layout> ); @@ -292,6 +178,9 @@ export const pageQuery = graphql` prerequisites description } + problems { + uniqueID + } } } } diff --git a/src/utils/getProgressInfo.ts b/src/utils/getProgressInfo.ts new file mode 100644 index 0000000..567da18 --- /dev/null +++ b/src/utils/getProgressInfo.ts @@ -0,0 +1,25 @@ +const getProgressInfo = ( + keys: string[], + data: { [key: string]: string }, + completedValues: string[], + inProgressValues: string[], + skippedValues: string[], + notStartedValues: string[] +) => { + let res = { + completed: 0, + inProgress: 0, + skipped: 0, + notStarted: 0, + }; + for (let key of keys) { + if (!(key in data)) res.notStarted++; + else if (completedValues.includes(data[key])) res.completed++; + else if (inProgressValues.includes(data[key])) res.inProgress++; + else if (skippedValues.includes(data[key])) res.skipped++; + else if (notStartedValues.includes(data[key])) res.notStarted++; + } + return res; +}; + +export default getProgressInfo; diff --git a/src/utils.ts b/src/utils/utils.ts index d2a302d..d8b294b 100644 --- a/src/utils.ts +++ b/src/utils/utils.ts @@ -1,8 +1,8 @@ -import ModuleOrdering, { moduleIDToDivisionMap } from '../content/ordering'; -import { ModuleInfo, ModuleLinkInfo } from './module'; +import MODULE_ORDERING from '../../content/ordering'; +import { ModuleInfo, ModuleLinkInfo } from '../models/module'; export const getModule = (allModules, division) => { - return ModuleOrdering[division].map(k => { + return MODULE_ORDERING[division].map(k => { // rip spaghetti code, clean this up if (typeof k === 'object') { return { @@ -16,6 +16,7 @@ export const getModule = (allModules, division) => { slug: `/${division}/${allModules[k2 as string].frontmatter.id}`, }; }), + description: k.description, }; } else { if (!allModules.hasOwnProperty(k)) { @@ -29,29 +30,27 @@ export const getModule = (allModules, division) => { }); }; -export function graphqlToModuleLinks( - allMdx: any -): { [moduleID: string]: ModuleLinkInfo } { - return allMdx.edges.reduce((acc, cur) => { - acc[cur.node.frontmatter.id] = new ModuleLinkInfo( - cur.node.frontmatter.id, - moduleIDToDivisionMap[cur.node.frontmatter.id], - cur.node.frontmatter.title - ); - return acc; - }, {}); +export function graphqlToModuleLinks(allMdx: any): ModuleLinkInfo[] { + return allMdx.edges.map( + cur => + new ModuleLinkInfo( + cur.node.frontmatter.id, + cur.node.fields.division, + cur.node.frontmatter.title + ) + ); } export function graphqlToModuleInfo(mdx: any): ModuleInfo { - console.log(mdx.frontmatter); return new ModuleInfo( mdx.frontmatter.id, - moduleIDToDivisionMap[mdx.frontmatter.id], + mdx.fields.division, mdx.frontmatter.title, mdx.body, mdx.frontmatter.author, mdx.frontmatter.prerequisites, mdx.frontmatter.description, - mdx.frontmatter.frequency + mdx.frontmatter.frequency, + mdx.toc ); } diff --git a/static/CPH.pdf b/static/CPH.pdf Binary files differnew file mode 100644 index 0000000..7ddd8e9 --- /dev/null +++ b/static/CPH.pdf diff --git a/tailwind.config.js b/tailwind.config.js index f071cba..c931362 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,6 +5,7 @@ module.exports = { './src/**/*.js', './src/**/*.ts', './src/**/*.tsx', + './content/**/*.mdx', './gatsby-browser.tsx', ], }, @@ -9,6 +9,13 @@ dependencies: "@babel/highlight" "^7.10.3" +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/compat-data@^7.10.1", "@babel/compat-data@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.3.tgz#9af3e033f36e8e2d6e47570db91e64a846f5d382" @@ -62,6 +69,28 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.5.tgz#1f15e2cca8ad9a1d78a38ddba612f5e7cdbbd330" + integrity sha512-O34LQooYVDXPl7QWCdW9p4NR+QlzOr7xShPPJz8GsuCU3/8ua/wqTr7gmnxXv+WBESiGU/G5s16i6tUvHkNb+w== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.10.5" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.10.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.5" + "@babel/types" "^7.10.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@^7.10.1", "@babel/generator@^7.10.3", "@babel/generator@^7.5.0", "@babel/generator@^7.9.6": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.3.tgz#32b9a0d963a71d7a54f5f6c15659c3dbc2a523a5" @@ -72,6 +101,32 @@ lodash "^4.17.13" source-map "^0.5.0" +"@babel/generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.4.tgz#e49eeed9fe114b62fa5b181856a43a5e32f5f243" + integrity sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng== + dependencies: + "@babel/types" "^7.10.4" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/generator@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.5.tgz#1b903554bc8c583ee8d25f1e8969732e6b829a69" + integrity sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig== + dependencies: + "@babel/types" "^7.10.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.0.0": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-annotate-as-pure@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz#f6d08acc6f70bbd59b436262553fb2e259a1a268" @@ -162,6 +217,15 @@ "@babel/template" "^7.10.3" "@babel/types" "^7.10.3" +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-get-function-arity@^7.10.1", "@babel/helper-get-function-arity@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.3.tgz#3a28f7b28ccc7719eacd9223b659fdf162e4c45e" @@ -169,6 +233,13 @@ dependencies: "@babel/types" "^7.10.3" +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-hoist-variables@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.3.tgz#d554f52baf1657ffbd7e5137311abc993bb3f068" @@ -183,6 +254,20 @@ dependencies: "@babel/types" "^7.10.3" +"@babel/helper-member-expression-to-functions@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz#172f56e7a63e78112f3a04055f24365af702e7ee" + integrity sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA== + dependencies: + "@babel/types" "^7.10.5" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-module-imports@^7.10.1", "@babel/helper-module-imports@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.3.tgz#766fa1d57608e53e5676f23ae498ec7a95e1b11a" @@ -203,6 +288,19 @@ "@babel/types" "^7.10.1" lodash "^4.17.13" +"@babel/helper-module-transforms@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz#120c271c0b3353673fcdfd8c053db3c544a260d6" + integrity sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" + "@babel/helper-optimise-call-expression@^7.10.1", "@babel/helper-optimise-call-expression@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.3.tgz#f53c4b6783093195b0f69330439908841660c530" @@ -210,6 +308,13 @@ dependencies: "@babel/types" "^7.10.3" +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-plugin-utils@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" @@ -248,6 +353,16 @@ "@babel/traverse" "^7.10.1" "@babel/types" "^7.10.1" +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-simple-access@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" @@ -256,6 +371,14 @@ "@babel/template" "^7.10.1" "@babel/types" "^7.10.1" +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-split-export-declaration@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" @@ -263,11 +386,23 @@ dependencies: "@babel/types" "^7.10.1" +"@babel/helper-split-export-declaration@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz#2c70576eaa3b5609b24cb99db2888cc3fc4251d1" + integrity sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-validator-identifier@^7.10.1", "@babel/helper-validator-identifier@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz#60d9847f98c4cea1b279e005fdb7c28be5412d15" integrity sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + "@babel/helper-wrap-function@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" @@ -287,6 +422,15 @@ "@babel/traverse" "^7.10.1" "@babel/types" "^7.10.1" +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/highlight@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.3.tgz#c633bb34adf07c5c13156692f5922c81ec53f28d" @@ -296,6 +440,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@7.10.2": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" @@ -306,6 +459,16 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.3.tgz#7e71d892b0d6e7d04a1af4c3c79d72c1f10f5315" integrity sha512-oJtNJCMFdIMwXGmx+KxuaD7i3b8uS7TTFYW/FNG2BT8m+fmGHoiPYoH0Pe3gya07WuFmM5FCDIr1x0irkD/hyA== +"@babel/parser@^7.10.2", "@babel/parser@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.4.tgz#9eedf27e1998d87739fb5028a5120557c06a1a64" + integrity sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA== + +"@babel/parser@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" + integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== + "@babel/plugin-proposal-async-generator-functions@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.3.tgz#5a02453d46e5362e2073c7278beab2e53ad7d939" @@ -956,6 +1119,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c" + integrity sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/standalone@^7.10.3": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.10.3.tgz#aaabbf0fcfc82d595d3e1b9467b201d1a33a6e64" @@ -970,6 +1140,15 @@ "@babel/parser" "^7.10.3" "@babel/types" "^7.10.3" +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/traverse@7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.1.tgz#bbcef3031e4152a6c0b50147f4958df54ca0dd27" @@ -1000,6 +1179,36 @@ globals "^11.1.0" lodash "^4.17.13" +"@babel/traverse@^7.10.4", "@babel/traverse@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.5.tgz#77ce464f5b258be265af618d8fddf0536f20b564" + integrity sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.10.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/parser" "^7.10.5" + "@babel/types" "^7.10.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/traverse@^7.4.5": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.4.tgz#e642e5395a3b09cc95c8e74a27432b484b697818" + integrity sha512-aSy7p5THgSYm4YyxNGz6jZpXf+Ok40QF3aA2LyIONkDHpAcJzDUqlCKXv6peqYUs2gmic849C/t2HKw2a2K20Q== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + "@babel/types@7.10.2": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" @@ -1018,6 +1227,46 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.4.tgz#369517188352e18219981efd156bfdb199fff1ee" + integrity sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@babel/types@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.5.tgz#d88ae7e2fde86bfbfe851d4d81afa70a997b5d15" + integrity sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@emotion/is-prop-valid@^0.8.8": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@emotion/stylis@^0.8.4": + version "0.8.5" + resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" + integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== + +"@emotion/unitless@^0.7.4": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + "@fullhuman/postcss-purgecss@^2.1.2": version "2.3.0" resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz#50a954757ec78696615d3e118e3fee2d9291882e" @@ -1718,6 +1967,11 @@ native-url "^0.2.6" schema-utils "^2.6.5" +"@popperjs/core@^2.3.2": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.4.tgz#11d5db19bd178936ec89cd84519c4de439574398" + integrity sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg== + "@reach/router@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.3.tgz#58162860dce6c9449d49be86b0561b5ef46d80db" @@ -1728,6 +1982,58 @@ prop-types "^15.6.1" react-lifecycles-compat "^3.0.4" +"@sentry/browser@latest": + version "5.19.2" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.19.2.tgz#8bad445b8d1efd50e6510bb43b3018b941f6e5cb" + integrity sha512-o6Z532n+0N5ANDzgR9GN+Q6CU7zVlIJvBEW234rBiB+ZZj6XwTLS1dD+JexGr8lCo8PeXI2rypKcj1jUGLVW8w== + dependencies: + "@sentry/core" "5.19.2" + "@sentry/types" "5.19.2" + "@sentry/utils" "5.19.2" + tslib "^1.9.3" + +"@sentry/core@5.19.2": + version "5.19.2" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.19.2.tgz#99a64ef0e55230fc02a083c48fa07ada85de4929" + integrity sha512-sfbBsVXpA0WYJUichz5IhvqKD8xJUfQvsszrTsUKa7PQAMAboOmuh6bo8KquaVQnAZyZWZU08UduvlSV3tA7tw== + dependencies: + "@sentry/hub" "5.19.2" + "@sentry/minimal" "5.19.2" + "@sentry/types" "5.19.2" + "@sentry/utils" "5.19.2" + tslib "^1.9.3" + +"@sentry/hub@5.19.2": + version "5.19.2" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.19.2.tgz#ab7f3d2d253c3441b2833a530b17c6de2418b2c7" + integrity sha512-2KkEYX4q9TDCOiaVEo2kQ1W0IXyZxJxZtIjDdFQyes9T4ubYlKHAbvCjTxHSQv37lDO4t7sOIApWG9rlkHzlEA== + dependencies: + "@sentry/types" "5.19.2" + "@sentry/utils" "5.19.2" + tslib "^1.9.3" + +"@sentry/minimal@5.19.2": + version "5.19.2" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.19.2.tgz#0fc2fdf9911a0cb31b52f7ccad061b74785724a3" + integrity sha512-rApEOkjy+ZmkeqEItgFvUFxe5l+dht9AumuUzq74pWp+HJqxxv9IVTusKppBsE1adjtmyhwK4O3Wr8qyc75xlw== + dependencies: + "@sentry/hub" "5.19.2" + "@sentry/types" "5.19.2" + tslib "^1.9.3" + +"@sentry/types@5.19.2": + version "5.19.2" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.19.2.tgz#ead586f0b64b91c396d3521b938ca25f7b59d655" + integrity sha512-O6zkW8oM1qK5Uma9+B/UMlmlm9/gkw9MooqycWuEhIaKfDBj/yVbwb/UTiJmNkGc5VJQo0v1uXUZZQt6/Xq1GA== + +"@sentry/utils@5.19.2": + version "5.19.2" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.19.2.tgz#f2819d9de5abc33173019e81955904247e4a8246" + integrity sha512-gEPkC0CJwvIWqcTcPSdIzqJkJa9N5vZzUZyBvdu1oiyJu7MfazpJEvj3whfJMysSfXJQxoJ+a1IPrA73VY23VA== + dependencies: + "@sentry/types" "5.19.2" + tslib "^1.9.3" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1763,6 +2069,13 @@ hex-rgb "^4.1.0" postcss-selector-parser "^6.0.2" +"@tippyjs/react@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.1.0.tgz#be4e826ac198d2394a5ffed3508ca9c098c527f1" + integrity sha512-g6Dpm46edr9T9z+BYxd/eJZa6QMFc4T4z5xrztxVlkti7AhNYf7OaE6b3Nh+boUZZ9wn8xkNq9VrQM5K4huwnQ== + dependencies: + tippy.js "^6.2.0" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -2219,7 +2532,7 @@ acorn-node@^1.6.1: acorn-walk "^7.0.0" xtend "^4.0.2" -acorn-walk@^7.0.0: +acorn-walk@^7.0.0, acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== @@ -2818,6 +3131,21 @@ babel-plugin-remove-graphql-queries@^2.9.7: resolved "https://registry.yarnpkg.com/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.9.7.tgz#81e557c36811f5a6c0166c5da84d6178fbfba632" integrity sha512-PnwHO+l5fJ+xq2oKF1+moMiWRJFcb29RSLcWiVzqVxDU4VzZapvmGY9qMFzFWS0txGMzlknBNxTL4Pigd/JyZg== +"babel-plugin-styled-components@>= 1", babel-plugin-styled-components@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz#3494e77914e9989b33cc2d7b3b29527a949d635c" + integrity sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-module-imports" "^7.0.0" + babel-plugin-syntax-jsx "^6.18.0" + lodash "^4.17.11" + +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= + babel-plugin-syntax-object-rest-spread@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" @@ -2916,6 +3244,11 @@ bail@^1.0.0: resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg= + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -2989,6 +3322,16 @@ better-queue@^3.8.10: node-eta "^0.9.0" uuid "^3.0.0" +bfj@^6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" + integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== + dependencies: + bluebird "^3.5.5" + check-types "^8.0.3" + hoopy "^0.1.4" + tryer "^1.0.1" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -3520,6 +3863,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelize@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" + integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -3588,7 +3936,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== @@ -3655,6 +4003,11 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +check-types@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" + integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== + cheerio@^0.22.0: version "0.22.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" @@ -3763,6 +4116,16 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.2.5, classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + +clean-set@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/clean-set/-/clean-set-1.1.2.tgz#76d8bf238c3e27827bfa73073ecdfdc767187070" + integrity sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug== + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -3952,7 +4315,7 @@ command-exists@^1.2.4: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@^2.19.0, commander@^2.20.0, commander@^2.20.3: +commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4207,7 +4570,7 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== -core-js@2, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.6.11, core-js@^2.6.5: +core-js@2, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.6.10, core-js@^2.6.11, core-js@^2.6.5: version "2.6.11" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== @@ -4367,6 +4730,11 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= + css-color-names@0.0.4, css-color-names@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" @@ -4437,6 +4805,15 @@ css-selector-tokenizer@^0.7.0: fastparse "^1.1.2" regexpu-core "^4.6.0" +css-to-react-native@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" + integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== + dependencies: + camelize "^1.0.0" + css-color-keywords "^1.0.0" + postcss-value-parser "^4.0.2" + css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" @@ -4579,6 +4956,69 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +d3-array@^1.2.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" + integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== + +d3-collection@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" + integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== + +d3-color@1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" + integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== + +d3-format@1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.4.tgz#356925f28d0fd7c7983bfad593726fce46844030" + integrity sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw== + +d3-interpolate@1, d3-interpolate@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" + integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== + dependencies: + d3-color "1" + +d3-path@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-scale@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" + integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw== + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + +d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +d3-time-format@2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.2.3.tgz#0c9a12ee28342b2037e5ea1cf0b9eb4dd75f29cb" + integrity sha512-RAHNnD8+XvC4Zc4d2A56Uw0yJoM7bsvOlJR33bclxq399Rak/b9bhvu/InjxdWhPtkgU53JJcleJTGkNRnN6IA== + dependencies: + d3-time "1" + +d3-time@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" + integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -4647,6 +5087,11 @@ decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.0.tgz#ca7faf504c799326df94b0ab920424fdfc125348" + integrity sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -4983,6 +5428,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -5032,6 +5482,13 @@ dom-converter@^0.2: dependencies: utila "~0.4" +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + dom-helpers@^5.0.1: version "5.1.4" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b" @@ -5176,6 +5633,11 @@ download@^7.1.0: p-event "^2.1.0" pify "^3.0.0" +dset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/dset/-/dset-2.0.1.tgz#a15fff3d1e4d60ac0c95634625cbd5441a76deb1" + integrity sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ== + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -5209,6 +5671,11 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +ejs@^2.6.1: + version "2.7.4" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" + integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + electron-to-chromium@^1.3.413: version "1.3.480" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.480.tgz#190ae45074578349a4c4f336fba29e76b20e9ef5" @@ -5845,7 +6312,7 @@ express-graphql@^0.9.0: http-errors "^1.7.3" raw-body "^2.4.1" -express@^4.17.1: +express@^4.16.3, express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -6179,6 +6646,11 @@ filesize@3.5.11: resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.11.tgz#1919326749433bb3cf77368bd158caabcc19e9ee" integrity sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g== +filesize@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -6541,6 +7013,22 @@ gatsby-page-utils@^0.2.12: lodash "^4.17.15" micromatch "^3.1.10" +gatsby-plugin-catch-links@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/gatsby-plugin-catch-links/-/gatsby-plugin-catch-links-2.3.11.tgz#6a61a126a2ffcc3b155a1647123eb069a0b9e5d6" + integrity sha512-30oNCe85evOmyEQ4THwfV0Uokv0GVQv+VpR6ztabLDF78J3p3yFQNQhtcyXPjHrLU2EeuWutSZYk3v1etdh9Hw== + dependencies: + "@babel/runtime" "^7.10.3" + escape-string-regexp "^1.0.5" + +gatsby-plugin-google-analytics@^2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/gatsby-plugin-google-analytics/-/gatsby-plugin-google-analytics-2.3.10.tgz#f0c31d29e35a4d3185128d799040ba533196dc6c" + integrity sha512-PnGIluGbz2xLEe9+dz5ozMiw9HPBNwCR8VeJMoznRNcl+/JW2dZ5NEhtbrGIDx3JOuGjwOXbZZqbLL4UKZXDYQ== + dependencies: + "@babel/runtime" "^7.10.3" + minimatch "3.0.4" + gatsby-plugin-manifest@^2.4.14: version "2.4.14" resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-2.4.14.tgz#9d82fbf81fc568860b1b3cc8cc0bf6cd8513b229" @@ -6633,6 +7121,13 @@ gatsby-plugin-react-helmet@^3.3.6: dependencies: "@babel/runtime" "^7.10.3" +gatsby-plugin-sentry@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gatsby-plugin-sentry/-/gatsby-plugin-sentry-1.0.1.tgz#d146e3851488c9545f2c0689cad62bf6e7d1c653" + integrity sha512-rKIngbFbBwwGYJi1ayUtEz34tXxpLXvSMD3WWVmdtXs0wrYrlGs30nFJ4JJIxcA7fWtx3hQ2yd5O2ZyQ832G2g== + dependencies: + "@sentry/browser" latest + gatsby-plugin-sharp@^2.6.14: version "2.6.14" resolved "https://registry.yarnpkg.com/gatsby-plugin-sharp/-/gatsby-plugin-sharp-2.6.14.tgz#92b1e69a57dcbd316570ccfc58ab4e03bfa252c4" @@ -6658,6 +7153,13 @@ gatsby-plugin-sharp@^2.6.14: svgo "1.3.2" uuid "^3.4.0" +gatsby-plugin-styled-components@^3.3.10: + version "3.3.10" + resolved "https://registry.yarnpkg.com/gatsby-plugin-styled-components/-/gatsby-plugin-styled-components-3.3.10.tgz#d325535023695382cf0f209382e5e0cc4658e6e6" + integrity sha512-2yV3hanEPelf8IwkOa1Sk4RtHh4tuuvdJs2NCnAsHxYEMLlFC4UeG91z4Q4t69G7RvZ2W8PzdByLK5N5C97CQQ== + dependencies: + "@babel/runtime" "^7.10.3" + gatsby-plugin-typescript@^2.4.8: version "2.4.8" resolved "https://registry.yarnpkg.com/gatsby-plugin-typescript/-/gatsby-plugin-typescript-2.4.8.tgz#d39fe16ee80ed38f736a93b040a80cec55014aa8" @@ -6671,6 +7173,14 @@ gatsby-plugin-typescript@^2.4.8: "@babel/runtime" "^7.10.3" babel-plugin-remove-graphql-queries "^2.9.7" +gatsby-plugin-webpack-bundle-analyser-v2@^1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/gatsby-plugin-webpack-bundle-analyser-v2/-/gatsby-plugin-webpack-bundle-analyser-v2-1.1.10.tgz#756ad6c7e01387d6044a26d7f7f6821a8e31f49a" + integrity sha512-VQtMIRcflAlMpauQZy/VnCb5SzTWhYw1jKZaVwAm+oaIdHav8xCAyg9hoQ849x46dUdk8/H3dnz5U6h+Z2MbLA== + dependencies: + "@babel/runtime" "^7.10.1" + webpack-bundle-analyzer "^3.8.0" + gatsby-react-router-scroll@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/gatsby-react-router-scroll/-/gatsby-react-router-scroll-3.0.7.tgz#53e9e4e81dfde94c23d724f8e6b4778db9d7edb1" @@ -6765,6 +7275,23 @@ gatsby-remark-custom-blocks@^2.3.6: "@babel/runtime" "^7.10.3" remark-custom-blocks "^2.5.0" +gatsby-remark-images@^3.3.14: + version "3.3.14" + resolved "https://registry.yarnpkg.com/gatsby-remark-images/-/gatsby-remark-images-3.3.14.tgz#584bd6f168665f9af2508bddca36d77ef6e7081b" + integrity sha512-8GxTek3HlwOlOEhEdUlLVdf3azI7BsgQCoVqNhRIGAF7rP30LpmF8ZK0tSSh3FyCoKI7uAaREQMbL9DLguoCCw== + dependencies: + "@babel/runtime" "^7.10.3" + chalk "^2.4.2" + cheerio "^1.0.0-rc.3" + gatsby-core-utils "^1.3.8" + is-relative-url "^3.0.0" + lodash "^4.17.15" + mdast-util-definitions "^1.2.5" + potrace "^2.1.6" + query-string "^6.13.1" + unist-util-select "^1.5.0" + unist-util-visit-parents "^2.1.2" + gatsby-remark-katex@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/gatsby-remark-katex/-/gatsby-remark-katex-3.3.6.tgz#fd7b58aa982592569f0a1ef698354fe136bf3f9e" @@ -7455,6 +7982,14 @@ gzip-size@3.0.0: dependencies: duplexer "^0.1.1" +gzip-size@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== + dependencies: + duplexer "^0.1.1" + pify "^4.0.1" + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -7802,7 +8337,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -7816,6 +8351,11 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" +hoopy@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" + integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== + hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -9413,7 +9953,7 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= -lodash.merge@^4.4.0: +lodash.merge@^4.4.0, lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== @@ -9473,6 +10013,11 @@ lodash@4.17.15, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.13 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.19, lodash@~4.17.4: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + log-symbols@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" @@ -9680,6 +10225,11 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" +math-expression-evaluator@^1.2.14: + version "1.2.22" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.22.tgz#c14dcb3d8b4d150e5dcea9c68c8dad80309b0d5e" + integrity sha512-L0j0tFVZBQQLeEjmWOvDLoRciIY8gQGWahvkztXUal8jH8R5Rlqo9GCvgqvXcy9LQhEWdQCVvzqAbxgYNt4blQ== + md5-file@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-3.2.3.tgz#f9bceb941eca2214a4c0727f5e700314e770f06f" @@ -9726,13 +10276,20 @@ mdast-util-compact@^2.0.0: dependencies: unist-util-visit "^2.0.0" -mdast-util-definitions@^1.2.0: +mdast-util-definitions@^1.2.0, mdast-util-definitions@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-1.2.5.tgz#3fe622a4171c774ebd06f11e9f8af7ec53ea5c74" integrity sha512-CJXEdoLfiISCDc2JB6QLb79pYfI6+GcIH+W2ox9nMc7od0Pz+bovcHsiq29xAQY6ayqe/9CsK2VzkSJdg1pFYA== dependencies: unist-util-visit "^1.0.0" +mdast-util-definitions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-2.0.1.tgz#2c931d8665a96670639f17f98e32c3afcfee25f3" + integrity sha512-Co+DQ6oZlUzvUR7JCpP249PcexxygiaKk9axJh+eRzHDZJk2julbIdKB4PXHVxdBuLzvJ1Izb+YDpj2deGMOuA== + dependencies: + unist-util-visit "^2.0.0" + mdast-util-definitions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-3.0.1.tgz#06af6c49865fc63d6d7d30125569e2f7ae3d0a86" @@ -10010,7 +10567,7 @@ minimatch@3.0.3: dependencies: brace-expansion "^1.0.0" -minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -10641,6 +11198,11 @@ opencollective-postinstall@^2.0.2: resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== +opener@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" + integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== + opentracing@^0.14.4: version "0.14.4" resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.4.tgz#a113408ea740da3a90fde5b3b0011a375c2e4268" @@ -11858,7 +12420,7 @@ prompts@^2.3.2: kleur "^3.0.3" sisteransi "^1.0.4" -prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -12041,6 +12603,13 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== +raf@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -12199,11 +12768,41 @@ react-refresh@^0.7.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.7.2.tgz#f30978d21eb8cac6e2f2fde056a7d04f6844dd50" integrity sha512-u5l7fhAJXecWUJzVxzMRU2Zvw8m4QmDNHlTrT5uo3KBlYBhmChd7syAakBoay1yIiVhx/8Fi7a6v6kQZfsw81Q== +react-resize-detector@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-2.3.0.tgz#57bad1ae26a28a62a2ddb678ba6ffdf8fa2b599c" + integrity sha512-oCAddEWWeFWYH5FAcHdBYcZjAw9fMzRUK9sWSx6WvSSOPVRxcHd5zTIGy/mOus+AhN/u6T4TMiWxvq79PywnJQ== + dependencies: + lodash.debounce "^4.0.8" + lodash.throttle "^4.1.1" + prop-types "^15.6.0" + resize-observer-polyfill "^1.5.0" + react-side-effect@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.0.tgz#1ce4a8b4445168c487ed24dab886421f74d380d3" integrity sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg== +react-smooth@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.5.tgz#94ae161d7951cdd893ccb7099d031d342cb762ad" + integrity sha512-eW057HT0lFgCKh8ilr0y2JaH2YbNcuEdFpxyg7Gf/qDKk9hqGMyXryZJ8iMGJEuKH0+wxS0ccSsBBB3W8yCn8w== + dependencies: + lodash "~4.17.4" + prop-types "^15.6.0" + raf "^3.4.0" + react-transition-group "^2.5.0" + +react-transition-group@^2.5.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + react-transition-group@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" @@ -12320,6 +12919,30 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" +recharts-scale@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.3.tgz#040b4f638ed687a530357292ecac880578384b59" + integrity sha512-t8p5sccG9Blm7c1JQK/ak9O8o95WGhNXD7TXg/BW5bYbVlr6eCeRBNpgyigD4p6pSSMehC5nSvBUPj6F68rbFA== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^1.8.5: + version "1.8.5" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-1.8.5.tgz#ca94a3395550946334a802e35004ceb2583fdb12" + integrity sha512-tM9mprJbXVEBxjM7zHsIy6Cc41oO/pVYqyAsOHLxlJrbNBuLs0PHB3iys2M+RqCF0//k8nJtZF6X6swSkWY3tg== + dependencies: + classnames "^2.2.5" + core-js "^2.6.10" + d3-interpolate "^1.3.0" + d3-scale "^2.1.0" + d3-shape "^1.2.0" + lodash "^4.17.5" + prop-types "^15.6.0" + react-resize-detector "^2.3.0" + react-smooth "^1.0.5" + recharts-scale "^0.4.2" + reduce-css-calc "^1.3.0" + recursive-readdir@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99" @@ -12335,6 +12958,15 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +reduce-css-calc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY= + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + reduce-css-calc@^2.1.6: version "2.1.7" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2" @@ -12343,6 +12975,13 @@ reduce-css-calc@^2.1.6: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" +reduce-function-call@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz#60350f7fb252c0a67eb10fd4694d16909971300f" + integrity sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ== + dependencies: + balanced-match "^1.0.0" + redux-thunk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" @@ -12504,6 +13143,17 @@ remark-custom-blocks@^2.5.0: dependencies: space-separated-tokens "^1.1.5" +remark-external-links@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/remark-external-links/-/remark-external-links-6.1.0.tgz#1a545b3cf896eae00ec1732d90f595f75a329abe" + integrity sha512-dJr+vhe3wuh1+E9jltQ+efRMqtMDOOnfFkhtoArOmhnBcPQX6THttXMkc/H0kdnAvkXTk7f2QdOYm5qo/sGqdw== + dependencies: + extend "^3.0.0" + is-absolute-url "^3.0.0" + mdast-util-definitions "^2.0.0" + space-separated-tokens "^1.0.0" + unist-util-visit "^2.0.0" + remark-footnotes@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-1.0.0.tgz#9c7a97f9a89397858a50033373020b1ea2aad011" @@ -12734,6 +13384,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +resize-observer-polyfill@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -13769,6 +14424,11 @@ string-similarity@^1.2.2: lodash.map "^4.6.0" lodash.maxby "^4.6.0" +string-similarity@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.1.tgz#ea7c11f0093cb3088cdcc5eb16cfd90cb54962f7" + integrity sha512-v36MJzloekKVvKAsYi6O/qpn2mIuvwEFIT9Gx3yg4spkNjXYsk7yxc37g4ZTyMVIBvt/9PZGxnqEtme8XHK+Mw== + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -14002,6 +14662,22 @@ style-to-object@^0.2.1: dependencies: inline-style-parser "0.1.1" +styled-components@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.1.1.tgz#96dfb02a8025794960863b9e8e365e3b6be5518d" + integrity sha512-1ps8ZAYu2Husx+Vz8D+MvXwEwvMwFv+hqqUwhNlDN5ybg6A+3xyW1ECrAgywhvXapNfXiz79jJyU0x22z0FFTg== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/traverse" "^7.4.5" + "@emotion/is-prop-valid" "^0.8.8" + "@emotion/stylis" "^0.8.4" + "@emotion/unitless" "^0.7.4" + babel-plugin-styled-components ">= 1" + css-to-react-native "^3.0.0" + hoist-non-react-statics "^3.0.0" + shallowequal "^1.1.0" + supports-color "^5.5.0" + stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" @@ -14032,7 +14708,7 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -14108,7 +14784,7 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tailwindcss@^1.4.6: +tailwindcss@^1.4.4, tailwindcss@^1.4.6: version "1.4.6" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.4.6.tgz#17b37166ccda08d7e7f9ca995ea48ce1e0089700" integrity sha512-qV0qInUq1FWih39Bc5CWECdgObSzRrbjGD4ke4kAPSIq6WXrPhv0wwOcUWJgJ66ltT9j+XnSRYikG8WNRU/fTQ== @@ -14296,6 +14972,13 @@ tinycolor2@^1.4.1: resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= +tippy.js@^6.2.0, tippy.js@^6.2.4: + version "6.2.4" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.2.4.tgz#b76000080cc035745cab4f20ac7a30e1ccb1b6bb" + integrity sha512-S3qLJhx7cpeGDpHw411jU62W1RvOGPkt3r68y8nwPi7wm/aexrSYAADbVb1ZNYCjspEwLWQvtAx76COvPcLvCw== + dependencies: + "@popperjs/core" "^2.3.2" + title-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" @@ -14430,6 +15113,11 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== +tryer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" + integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== + ts-invariant@^0.4.0: version "0.4.4" resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86" @@ -14497,6 +15185,23 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +twin.macro@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/twin.macro/-/twin.macro-1.5.0.tgz#8ef869ee32e2e73b0f9954d412b1d7fe80519d70" + integrity sha512-LDdNwobwizgOXVwxLObALq1j+u9USFrGXpKPQy7UCum+y/8UQBxCnI+JV7NCRq4a/+3o3ot8TY0hmkIriuZP+w== + dependencies: + "@babel/parser" "^7.10.2" + babel-plugin-macros "^2.8.0" + chalk "^4.1.0" + clean-set "^1.1.1" + color "^3.1.2" + dlv "^1.1.3" + dset "^2.0.1" + lodash.merge "^4.6.2" + string-similarity "^4.0.1" + tailwindcss "^1.4.4" + timsort "^0.3.0" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -14818,7 +15523,7 @@ unist-util-visit-children@^1.0.0: resolved "https://registry.yarnpkg.com/unist-util-visit-children/-/unist-util-visit-children-1.1.4.tgz#e8a087e58a33a2815f76ea1901c15dec2cb4b432" integrity sha512-sA/nXwYRCQVRwZU2/tQWUqJ9JSFM1X3x7JIOsIgSzrFHcfVt6NkzDtKzyxg2cZWkCwGF9CO8x4QNZRJRMK8FeQ== -unist-util-visit-parents@^2.0.0: +unist-util-visit-parents@^2.0.0, unist-util-visit-parents@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== @@ -15187,6 +15892,25 @@ web-namespaces@^1.0.0, web-namespaces@^1.1.2: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +webpack-bundle-analyzer@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz#ce6b3f908daf069fd1f7266f692cbb3bded9ba16" + integrity sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + bfj "^6.1.1" + chalk "^2.4.1" + commander "^2.18.0" + ejs "^2.6.1" + express "^4.16.3" + filesize "^3.6.1" + gzip-size "^5.0.0" + lodash "^4.17.15" + mkdirp "^0.5.1" + opener "^1.5.1" + ws "^6.0.0" + webpack-dev-middleware@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" @@ -15607,7 +16331,7 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" -ws@^6.2.1: +ws@^6.0.0, ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== |