|
2 | 2 | id: usaco-646
|
3 | 3 | source: USACO Gold 2016 Open
|
4 | 4 | title: Closing the Farm
|
5 |
| -author: Nathan Gong, Melody Yu |
| 5 | +author: Nathan Gong, Melody Yu, George Pong |
6 | 6 | ---
|
7 | 7 |
|
8 | 8 | [Official Analysis](http://www.usaco.org/current/data/sol_closing_gold_open16.html)
|
9 | 9 |
|
| 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. |
10 | 17 |
|
11 | 18 | ## Video Solution
|
12 | 19 |
|
13 | 20 | Note: The video solution might not be the same as other solutions. Code in C++.
|
| 21 | + |
14 | 22 | <Youtube id="5G1CGuTfUJk" />
|
15 | 23 |
|
16 | 24 | ## Implementation
|
17 | 25 |
|
| 26 | +**Time Complexity:** $\mathcal{O}(V \cdot E \cdot \alpha(n)))$ |
| 27 | + |
18 | 28 | <LanguageSection>
|
19 | 29 |
|
| 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 | + |
20 | 96 | <JavaSection>
|
21 | 97 |
|
22 | 98 | ```java
|
@@ -50,7 +126,7 @@ public class closing {
|
50 | 126 | UnionFind graph = new UnionFind(n);
|
51 | 127 | boolean[] open = new boolean[n];
|
52 | 128 | ArrayList<String> answers = new ArrayList<>();
|
53 |
| - |
| 129 | + |
54 | 130 | // Process closings in reverse order:
|
55 | 131 | // Instead of closing each farm, we simulate opening farms
|
56 | 132 | for (int i = n - 1; i >= 0; i--) {
|
|
0 commit comments