Skip to content

Commit be25050

Browse files
Merge pull request #3163 from Amaryllidaceae/master
add solutions and editorials to usaco-646 and cses-1676
2 parents 2585ec4 + ef8b49a commit be25050

File tree

2 files changed

+144
-8
lines changed

2 files changed

+144
-8
lines changed

solutions/cses-1676.mdx

+66-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,25 @@
22
id: cses-1676
33
source: CSES
44
title: Road Construction
5-
author: Oscar Garries, Sofia Yang
5+
author: Oscar Garries, Sofia Yang, George Pong
66
---
77

8-
**Time Complexity:** $\mathcal{O}(N\log N)$
8+
## Explanation
99

10-
<LanguageSection>
10+
We can represent the problem as an undirected graph, with each of the $n$ cities being a node and each of the $m$ roads being an edge. We then have two subproblems we need to consider:
1111

12-
<CPPSection>
12+
Our first subproblem is counting the number of connected components each day. We can count them using Disjoint Set Union (DSU), where each tree in the structure represents a connected component. We start off with $n$ cities and no roads between them, and thus $n$ connected components. For every road that is built, we unite the two cities the road connects. If the union is successful, then two different connected components have been joined into one, and thus the number of connected components decreases by 1.
13+
14+
Our second subproblem is finding the size of the largest connected component each day. Conveniently, the DSU optimization union-by-size stores the size of any node's component in that tree's parent node. Furthermore, the largest component will only change if a union is successful, as otherwise the graph stays the same. If a union happens, we check if the size of the tree which was added to during uniting is a new maximum.
1315

1416
## Implementation
1517

18+
**Time Complexity:** $\mathcal{O}(M \cdot \alpha(N))$
19+
20+
<LanguageSection>
21+
22+
<CPPSection>
23+
1624
```cpp
1725
#include <bits/stdc++.h>
1826

@@ -56,9 +64,61 @@ int main () {
5664

5765
</CPPSection>
5866

59-
<JavaSection>
67+
<PySection>
68+
69+
```python
70+
import sys
71+
input = sys.stdin.readline # speed up input to not TLE
72+
73+
#BeginCodeSnip{Disjoint Set Union}
74+
class DisjointSetUnion:
75+
def __init__(self, num_nodes: int) -> None:
76+
self.parent = [*range(num_nodes)]
77+
self.size = [1] * num_nodes
78+
79+
def find_parent(self, v: int) -> int:
80+
if self.parent[v] == v:
81+
return v
82+
self.parent[v] = self.find_parent(self.parent[v])
83+
return self.parent[v]
84+
85+
def union(self, a: int, b: int) -> bool:
86+
a = self.find_parent(a)
87+
b = self.find_parent(b)
88+
if a == b:
89+
return False
90+
if self.size[a] < self.size[b]:
91+
a, b = b, a
92+
self.parent[b] = a
93+
self.size[a] += self.size[b]
94+
return True
95+
96+
def connected(self, a: int, b: int) -> bool:
97+
return self.find_parent(a) == self.find_parent(b)
98+
#EndCodeSnip
99+
100+
n, m = map(int, input().split())
101+
102+
cities = DisjointSetUnion(n)
103+
largest_size = 1
104+
components = n
105+
106+
for _ in range(m):
107+
a, b = map(lambda i: int(i) - 1, input().split())
108+
if cities.union(a, b):
109+
components -= 1
110+
111+
# a is the parent node when uniting in our dsu implementation
112+
size_a = cities.size[cities.find_parent(a)]
113+
if size_a > largest_size:
114+
largest_size = size_a
115+
116+
print(components, largest_size)
117+
```
60118
61-
## Implementation
119+
</PySection>
120+
121+
<JavaSection>
62122
63123
```java
64124
import java.io.BufferedReader;

solutions/usaco-646.mdx

+78-2
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,97 @@
22
id: usaco-646
33
source: USACO Gold 2016 Open
44
title: Closing the Farm
5-
author: Nathan Gong, Melody Yu
5+
author: Nathan Gong, Melody Yu, George Pong
66
---
77

88
[Official Analysis](http://www.usaco.org/current/data/sol_closing_gold_open16.html)
99

10+
## Explanation
11+
12+
We can represent the farm as an undirected graph, with each of the $n$ barns being a node, and each of the $m$ bidirectional paths being an edge. At each point in time, we want to check if the number of connected components is equal to one. Naively, this can be done with BFS or DFS. However, we would need to run this for all $n+1$ states of the barn, resulting in a TLE.
13+
14+
We can use Disjoint Set Union (DSU) to efficiently keep track of the number of connected components after each change. However, the key part of this solution is that, as DSU can only efficiently create relationships between nodes and not destroy them, we simulate closing the barn in reverse.
15+
16+
We start with an empty farm, and open barns in the reverse order they were closed in. For each barn we open, we unite the new barn to all its adjacent open barns, of which we will keep track of while opening. This makes the farm at each state the same as if it was being closed. While opening a barn will initially increase the number of connected components by 1, each successful union will decrease it by 1 as well. Thus, each opening of a barn will increase the number of connected components by $1 - \texttt{number of successful unions}$. Finally, our answer from each opening can be reversed once again to follow the initial order of closing the barns.
1017

1118
## Video Solution
1219

1320
Note: The video solution might not be the same as other solutions. Code in C++.
21+
1422
<Youtube id="5G1CGuTfUJk" />
1523

1624
## Implementation
1725

26+
**Time Complexity:** $\mathcal{O}(V \cdot E \cdot \alpha(n)))$
27+
1828
<LanguageSection>
1929

30+
<PySection>
31+
32+
```py
33+
#BeginCodeSnip{Disjoint Set Union}
34+
class DisjointSetUnion:
35+
def __init__(self, num_nodes: int) -> None:
36+
self.parent = [*range(num_nodes)]
37+
self.size = [1] * num_nodes
38+
39+
def find_parent(self, v: int) -> int:
40+
if self.parent[v] == v:
41+
return v
42+
self.parent[v] = self.find_parent(self.parent[v])
43+
return self.parent[v]
44+
45+
def union(self, a: int, b: int) -> bool:
46+
a = self.find_parent(a)
47+
b = self.find_parent(b)
48+
if a == b:
49+
return False
50+
if self.size[a] < self.size[b]:
51+
a, b = b, a
52+
self.parent[b] = a
53+
self.size[a] += self.size[b]
54+
return True
55+
56+
def connected(self, a: int, b: int) -> bool:
57+
return self.find_parent(a) == self.find_parent(b)
58+
#EndCodeSnip
59+
60+
with open('closing.in', 'r') as infile:
61+
n, m = map(int, infile.readline().split())
62+
graph = [[] for _ in range(n)]
63+
for _ in range(m):
64+
f, t = map(lambda i: int(i) - 1, infile.readline().split())
65+
graph[f].append(t)
66+
graph[t].append(f)
67+
remove_order = [int(infile.readline()) - 1 for _ in range(n)]
68+
69+
dsu = DisjointSetUnion(n)
70+
71+
"""
72+
We simulate opening the farm in the reverse order of closing it. For each barn,
73+
we open it, and then connect it to any open adjacent barns, all while counting
74+
the number of connected components.
75+
"""
76+
77+
open_barns = set()
78+
components = 0
79+
fully_connected = []
80+
81+
for node in remove_order[::-1]:
82+
components += 1
83+
for adj in graph[node]:
84+
if adj in open_barns:
85+
if dsu.union(adj, node):
86+
components -= 1
87+
88+
fully_connected.append('YES' if components == 1 else 'NO')
89+
open_barns.add(node)
90+
91+
print(*fully_connected[::-1], sep='\n', file=open('closing.out', 'w'))
92+
```
93+
94+
</PySection>
95+
2096
<JavaSection>
2197

2298
```java
@@ -50,7 +126,7 @@ public class closing {
50126
UnionFind graph = new UnionFind(n);
51127
boolean[] open = new boolean[n];
52128
ArrayList<String> answers = new ArrayList<>();
53-
129+
54130
// Process closings in reverse order:
55131
// Instead of closing each farm, we simulate opening farms
56132
for (int i = n - 1; i >= 0; i--) {

0 commit comments

Comments
 (0)