This commit is contained in:
Nathan Wang 2020-07-19 09:37:33 -07:00
commit 36acc99729
12 changed files with 952 additions and 48 deletions

View file

@ -1,8 +1,8 @@
---
id: code-con
title: Code Conventions
author: Nathan Wang, Benjamin Qi, Michael Cao
description: "?"
author: Nathan Wang, Benjamin Qi, Michael Cao, Nathan Chen
description: "The conventions that this guide uses"
prerequisites:
- expected
- modules
@ -10,10 +10,26 @@ prerequisites:
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!
Everything should compile assuming that the templates below are included. If any code does not compile or is hard to read, submit a complaint using the "Contact Us" button.
<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>
@ -27,24 +43,61 @@ Everything should compile assuming that the templates below are included. If any
- 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:
```cpp
if (a == b) {
++a;
} else {
++b;
}
```
<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`.
- Explain the use of any not well known standard library functions with comments that haven't been introduced before like `__builtin_ffs()` or `tie(a, b, c)` in C++.
- 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>
See [C++ Tips & Tricks](./cpp-tips).
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>
@ -79,6 +132,8 @@ int main() {
<!-- 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.
@ -88,7 +143,26 @@ int main() {
</CPPSection>
<JavaSection />
<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>
</LanguageSection>

View file

@ -453,7 +453,7 @@ In general, it may be faster to store the answer all in a single `string` (C++)
<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, whether it be 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.
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>

View file

@ -1,7 +1,7 @@
---
id: running-code
title: Running Code
author: Benjamin Qi, Hankai Zhang, Anthony Wang, Nathan Wang
author: Benjamin Qi, Hankai Zhang, Anthony Wang, Nathan Wang, Nathan Chen
description: Options for running your language of choice.
prerequisites:
- choosing-lang
@ -42,7 +42,7 @@ You can also share code with [pastebin](https://pastebin.com/) or [hastebin](htt
<CPPSection>
You can run from the command line and use a text editor of your choice.
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
@ -269,6 +269,57 @@ Now you can easily compile and run `name.cpp` from the command line with `co nam
</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 comman,
operable program or batch file
```
If you get a list of command arguments for the `java` command, 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.

View file

@ -5,7 +5,7 @@ author: Siyong Huang, Benjamin Qi
prerequisites:
- sorting-custom
- intro-ordered
description: "?"
description: "More ways to write custom comparators in C++ and incorporating them into STL objects."
frequency: 1
---
@ -43,7 +43,6 @@ int main() {
}
```
## Comparator
```cpp
@ -67,10 +66,20 @@ int main() {
}
```
<Info title="Pro Tip" >
You can also use the following syntax to declare set `v` using a function
`set<Edge,decltype(*cmp)> v(cmp);`
</Info>
Lambda expressions also work:
```cpp
auto cmp = [](const Edge& x, const Edge& y) { return x.w < y.w; };
//The real type of cmp is function<bool(const Edge&, const Edge&)>
//auto is used for short
int main() {
int M = 4;
@ -83,7 +92,13 @@ int main() {
}
```
In this case, we can shorten the declaration of `v` by writing `set<Edge,decltype(cmp)> v(cmp);` instead.
<Info title="Pro Tip" >
You can also use the following syntax to declare set `v` using a lambda
`set<Edge,decltype(cmp)> v(cmp);`
</Info>
## Functors
@ -127,9 +142,17 @@ int main() {
}
```
<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>`. 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.
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>
@ -166,4 +189,17 @@ The following are all valid:
set<int,greater<int>> a;
map<int,string,greater<int>> b;
priority_queue<int,vector<int>,greater<int>> c;
```
```
<Warning title="Priority Queues">
In C++, priority queues are sorted in decreasing order.
Specifically, larger elements have higher 'priority' and are popped first.
If you want smaller elements to be at the front of the queue, two ways are listed below
1. Overload the (`<`) operator to output the opposite effect.
2. Overload the (`>`) operator properly and use the (`greater<T>`) functor.
</Warning>

View file

@ -24,23 +24,29 @@ export const metadata = {
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", "Very Hard", 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={metadata.problems.sample} />
< br/>
Aka **successor graph**.
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, 16.4 - Successor Graphs"></Resource>
</Resources>
## Implementation
### 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.
@ -82,6 +88,14 @@ int main()
**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
<Resources>
<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)
@ -105,12 +119,7 @@ pair<int, int> detect_cycle(int *next, int start_node) //return pair(length of c
}
```
<Resources>
<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>
<IncompleteSection />
## Problems
### Problems
<Problems problems={metadata.problems.general} />

View file

@ -102,8 +102,76 @@ int main() {
</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>
@ -144,4 +212,4 @@ Don't forget that once Bessie reaches the goal, she will ignore further commands
## Problems
<Problems problems={metadata.problems.general} />
<Problems problems={metadata.problems.general} />

View file

@ -27,6 +27,7 @@ export const metadata = {
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"),
],
}
}

View file

@ -47,11 +47,11 @@ Note: You may prefer to use this in place of DFS for computing connected compone
## Implementations
Check PAPS for the explanation. `e[x]` contains the negation of the size of $x$'s component if $x$ is the representative of its component, and the parent of $x$ otherwise.
<LanguageSection>
<CPPSection>
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>

View file

@ -1,7 +1,7 @@
---
id: toposort
title: "Topological Sort"
author: Benjamin Qi, Michael Cao
author: Benjamin Qi, Michael Cao, Nathan Chen
prerequisites:
- Gold - Breadth First Search
- Gold - Introduction to Dynamic Programming
@ -40,7 +40,7 @@ A [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) of a dir
## Tutorial
<Resources>
<Resource source="CSA" title="Topological Sorting" url="topological_sorting" starred>both BFS, DFS</Resource>
<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
@ -58,7 +58,73 @@ A [topological sort](https://en.wikipedia.org/wiki/Topological_sorting) of a dir
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.
(implementation)
<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 />
@ -82,11 +148,308 @@ $$
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]$.
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]$.
(implementation?)
Note that the implementation of this idea below uses Kahn's algorithm for topological sorting:
<IncompleteSection />
<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>
@ -96,4 +459,4 @@ or zero if $v$ has in-degree $0$. If we process the states in topological order,
## Problems
<Problems problems={metadata.problems.general} />
<Problems problems={metadata.problems.general} />

View file

@ -1,7 +1,7 @@
---
id: centroid
title: "Centroid Decomposition"
author: Benjamin Qi
author: Benjamin Qi, Siyong Huang
prerequisites:
- dfs
- SRQ
@ -14,7 +14,12 @@ import { Problem } from "../models";
export const metadata = {
problems: {
general: [
new Problem("CF", "Ciel the Commander", "problemset/problem/321/C", "Easy", false, ["Centroid"]),
new Problem("CF", "Ciel the Commander", "problemset/problem/321/C", "Easy", false, ["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"], ""),
@ -24,6 +29,12 @@ export const metadata = {
}
};
## 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>
@ -31,8 +42,56 @@ export const metadata = {
<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 />
<PySection />
</LanguageSection>
### Problems
<Problems problems={metadata.problems.general} />
*Note:* Unfortunately, it seems like constant factor is especially important for DMOJ. :|
*Note:* Unfortunately, it seems like constant factor is especially important for DMOJ. :|

View file

@ -24,6 +24,7 @@ export const metadata = {
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"], ""),
],
@ -49,4 +50,4 @@ export const metadata = {
## Problems
<Problems problems={metadata.problems.general} />
<Problems problems={metadata.problems.general} />

View file

@ -1,20 +1,262 @@
---
id: string-suffix
title: "String Suffix Structures"
author: Benjamin Qi
description: "?"
author: Benjamin Qi, Siyong Huang
description: "Suffix Automata, Suffix Trees, and (TBD) Palindromic Trees"
prerequisites:
- Platinum - String Searching
frequency: 0
---
export const metadata = {
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"], ""),
]
}
};
## 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
<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>
</Resources>
<Info>
Most 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.
</Info>
### Problems
<Problems problems={metadata.problems.auto} />
<Spoiler title="USACO Standing Out using Suffix Automata">
<!-- 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>
## 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
<Resources>
<Resource source="CF" title="Suffix Tree. Ukkonen's algorithm" url="blog/entry/16780">Explanation of Ukkonen's Suffix Tree Algorithm</Resource>
<Resource source="cp-algo" title="Suffix Tree. Ukkonen's Algorithm" url="string/suffix-tree-ukkonen.html">Implementation of Ukkonen's Algorithm</Resource>
</Resources>
### Problems
<Problems problems={metadata.problems.tree} />
### Generate Suffix Array
A suffix array can be generated by the suffix tree by taking the dfs traversal of the suffix tree.
<LanguageSection>
<CPPSection>
<!-- https://codeforces.com/edu/course/2/lesson/2/2/practice/contest/269103/submission/85759835 -->
```cpp
int N, sa[MN];//length of string, suffix array
struct edge
{
public:
int n, l, r;//node, edge covers s[l..r]
explicit operator bool() const {return n!=-1;}
} c[MN*2][26];
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>
<JavaSection />
<PySection />
</LanguageSection>
## Palindromic Tree
(Still don't know what these are!! Benq help!)
* String Suffix Structures
* [Suffix Automata](http://codeforces.com/blog/entry/20861)
* Suffix Tree
* [CF](http://codeforces.com/blog/entry/16780)
* [CF](https://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)