#!/usr/bin/env python3 import sys # An implementation of the Disjoint Set Union (DSU) data structure, # also known as Union-Find. It's used here to efficiently track # the connected components of the graph as we add edges. class DSU: """A simple DSU class with path compression.""" def __init__(self, n): # Initialize each node to be its own parent. self.parent = list(range(n)) def find(self, i): """Find the root of the set containing element i with path compression.""" if self.parent[i] == i: return i # Path compression: set the parent of i directly to the root. self.parent[i] = self.find(self.parent[i]) return self.parent[i] def union(self, i, j): """Merge the sets containing elements i and j.""" root_i = self.find(i) root_j = self.find(j) if root_i != root_j: self.parent[root_i] = root_j return True # Returns true if a merge happened return False # Returns false if they were already in the same set def solve(): """ Solves the Fell Walking problem by iterating through all possible minimum heights of a path and, for each, finding the smallest possible maximum height that completes a path from Hill 1 to Hill 2. """ try: # Fast I/O input = sys.stdin.readline n, m = map(int, input().split()) heights_input = list(map(int, input().split())) # Adjust heights to be 1-indexed for consistency with hill numbers heights = [-1] + heights_input adj = [[] for _ in range(n + 1)] for _ in range(m): a, b = map(int, input().split()) adj[a].append(b) adj[b].append(a) except (IOError, ValueError): # Handle potential empty input or parsing errors gracefully return # Create a sorted list of all unique hill heights unique_heights = sorted(list(set(heights[1:]))) # Map heights to the hills that have that height for quick lookup hills_by_height = {h: [] for h in unique_heights} for i in range(1, n + 1): hills_by_height[heights[i]].append(i) min_diff = float('inf') active_edges = [] active_hills = [False] * (n + 1) # The core idea is to iterate through all possible minimum heights (h_min) # for a valid path. We iterate from the highest possible h_min downwards. # This allows us to incrementally add hills and edges to our graph, which # is more efficient than removing them. for l in range(len(unique_heights) - 1, -1, -1): h_min = unique_heights[l] # Activate all hills that have the current h_min newly_activated_hills = hills_by_height[h_min] new_edges = [] for u in newly_activated_hills: active_hills[u] = True # For each newly activated hill, check its neighbors. If a neighbor # is also active, the edge between them is now part of our considered graph. for u in newly_activated_hills: for v in adj[u]: if active_hills[v]: # The "weight" of an edge for our purpose is the max height # of its two endpoints, as this determines the h_max of a path. weight = max(heights[u], heights[v]) new_edges.append((weight, u, v)) # Add the new edges to our list and re-sort. # This is the main bottleneck, but necessary to run Kruskal's algorithm. active_edges.extend(new_edges) active_edges.sort() # We can only find a path if both Hill 1 and Hill 2 are active. if not active_hills[1] or not active_hills[2]: continue # For the current h_min, find the smallest possible h_max that connects # Hill 1 and Hill 2. This is a classic Minimum Spanning Tree (MST) # problem: the path between any two nodes in an MST minimizes the # maximum edge weight along the path. We can find this by running # Kruskal's algorithm until Hill 1 and 2 are connected. dsu = DSU(n + 1) if dsu.find(1) != dsu.find(2): # Optimization: check before iterating for weight, u, v in active_edges: dsu.union(u, v) if dsu.find(1) == dsu.find(2): # As soon as they are connected, the weight of the edge that # connected them is our best h_max for the current h_min. h_max = weight min_diff = min(min_diff, h_max - h_min) break # Found best h_max for this h_min print(min_diff) if __name__ == "__main__": solve()