|
| 1 | +# [Problem 3607: Power Grid Maintenance](https://leetcode.com/problems/power-grid-maintenance/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +We have c stations and static undirected connections which partition the stations into connected components (power grids). Initially every station is online. Queries are two kinds: |
| 5 | +- [1, x]: ask which operational station will resolve the check for x. If x is online return x; otherwise return the smallest id online station in x's component (or -1 if none). |
| 6 | +- [2, x]: mark x offline. |
| 7 | + |
| 8 | +Observations: |
| 9 | +- The connectivity (components) never changes — taking nodes offline doesn't change component membership. |
| 10 | +- So we can compute components once (DSU / union-find). |
| 11 | +- For each component we need to maintain the set of currently-online station ids with fast removal and fast retrieval of the minimum id. |
| 12 | +- Operations are only deletions (mark offline) and queries for min element. A min-heap per component with lazy deletions (keep popping offline tops) looks simple and efficient. Alternatively a balanced BST (sorted set) per component would work but Python doesn't have one built-in. |
| 13 | + |
| 14 | +So plan: build DSU, create a heap (min-heap) of members for each component, maintain an online boolean array. For a type-1 query: if x is online return x; otherwise find its component, lazily pop from that component's heap until top is online (or empty). Return top or -1. For a type-2 query: mark x offline (idempotent). |
| 15 | + |
| 16 | +## Refining the problem, round 2 thoughts |
| 17 | +Edge cases and details: |
| 18 | +- There may be repeated [2, x] operations; marking offline twice should be safe (we'll check online flag before setting, but setting to False again is harmless). |
| 19 | +- Heaps initially contain every member; lazy popping ensures each node is popped at most once across all queries, so overall pop complexity is O(c log c). |
| 20 | +- Queries involve DSU finds; with path compression and union by rank, find is effectively almost O(1) amortized (inverse-Ackermann). |
| 21 | +- Space: heaps total hold c items initially; online boolean array size c+1. |
| 22 | + |
| 23 | +Complexity: |
| 24 | +- Building DSU from connections: O(n * α(c)). |
| 25 | +- Building heaps: O(c) pushes, total O(c log c) to heapify incrementally. |
| 26 | +- Each query: find + potentially popping. Each item popped at most once so overall pop cost O(c log c); each query also does a constant number of heap/lookups. So total time O((c + n + q) log c) worst-case dominated by heap operations (more precisely O((c + number_of_pops) log c) and pops ≤ c). |
| 27 | +- Space O(c + n) for DSU and heaps. |
| 28 | + |
| 29 | +This approach is simple and should pass bounds (c ≤ 1e5, queries ≤ 2e5). |
| 30 | + |
| 31 | +## Attempted solution(s) |
| 32 | +```python |
| 33 | +import heapq |
| 34 | +from typing import List |
| 35 | + |
| 36 | +class DSU: |
| 37 | + def __init__(self, n): |
| 38 | + self.parent = list(range(n+1)) |
| 39 | + self.rank = [0]*(n+1) |
| 40 | + def find(self, x): |
| 41 | + while self.parent[x] != x: |
| 42 | + self.parent[x] = self.parent[self.parent[x]] |
| 43 | + x = self.parent[x] |
| 44 | + return x |
| 45 | + def union(self, a, b): |
| 46 | + ra = self.find(a) |
| 47 | + rb = self.find(b) |
| 48 | + if ra == rb: |
| 49 | + return |
| 50 | + if self.rank[ra] < self.rank[rb]: |
| 51 | + self.parent[ra] = rb |
| 52 | + elif self.rank[rb] < self.rank[ra]: |
| 53 | + self.parent[rb] = ra |
| 54 | + else: |
| 55 | + self.parent[rb] = ra |
| 56 | + self.rank[ra] += 1 |
| 57 | + |
| 58 | +class Solution: |
| 59 | + def powerGridMaintenance(self, c: int, connections: List[List[int]], queries: List[List[int]]) -> List[int]: |
| 60 | + dsu = DSU(c) |
| 61 | + for u, v in connections: |
| 62 | + dsu.union(u, v) |
| 63 | + # Build a heap for each component representative |
| 64 | + heaps = [None] * (c + 1) # heaps[rep] is a min-heap of node ids for that component |
| 65 | + for node in range(1, c+1): |
| 66 | + rep = dsu.find(node) |
| 67 | + if heaps[rep] is None: |
| 68 | + heaps[rep] = [] |
| 69 | + heapq.heappush(heaps[rep], node) |
| 70 | + online = [True] * (c + 1) # online[0] unused |
| 71 | + |
| 72 | + ans = [] |
| 73 | + for typ, x in queries: |
| 74 | + if typ == 1: |
| 75 | + if online[x]: |
| 76 | + ans.append(x) |
| 77 | + else: |
| 78 | + rep = dsu.find(x) |
| 79 | + heap = heaps[rep] |
| 80 | + # If there is no heap (shouldn't happen since every node was added), handle gracefully |
| 81 | + if heap is None: |
| 82 | + ans.append(-1) |
| 83 | + continue |
| 84 | + # lazy remove offline nodes |
| 85 | + while heap and not online[heap[0]]: |
| 86 | + heapq.heappop(heap) |
| 87 | + if heap: |
| 88 | + ans.append(heap[0]) |
| 89 | + else: |
| 90 | + ans.append(-1) |
| 91 | + else: # typ == 2 |
| 92 | + # mark offline (idempotent) |
| 93 | + if online[x]: |
| 94 | + online[x] = False |
| 95 | + return ans |
| 96 | + |
| 97 | +# The LeetCode entry point expects the method name; adapt if needed. |
| 98 | +# Example usage: |
| 99 | +# sol = Solution() |
| 100 | +# print(sol.powerGridMaintenance(5, [[1,2],[2,3],[3,4],[4,5]], [[1,3],[2,1],[1,1],[2,2],[1,2]])) |
| 101 | +``` |
| 102 | + |
| 103 | +- Notes about the solution approach: |
| 104 | + - Use DSU (union-find) to compute connected components once. |
| 105 | + - Maintain a min-heap per component containing all node ids initially. Because nodes are only ever removed (marked offline), lazy deletion on the heap (pop until the top is online) is efficient: each id is popped at most once overall. |
| 106 | + - For a [1, x] query, if x is online return x. Otherwise find component representative and retrieve the heap's current minimum after lazy cleanup; return -1 if heap becomes empty. |
| 107 | + - For a [2, x] query, mark x offline. No immediate heap modification is necessary. |
| 108 | +- Complexity: |
| 109 | + - Time: Building DSU O(n α(c)). Building heaps O(c log c) (pushes). Each node popped at most once across all queries -> total pop cost O(c log c). Each query performs O(α(c)) find plus occasional heap pops. So overall roughly O((c + n + q) log c) worst-case. |
| 110 | + - Space: O(c + n) for DSU structures and heaps (heaps store each node once). |
0 commit comments