Before attempting this problem, you should be comfortable with:
We need to keep both Alice and Bob connected across all nodes while removing as many edges as possible.
Type 3 edges (usable by both) are most valuable since they count toward connectivity for both users simultaneously.
We use two separate Union-Find structures to track connectivity for Alice and Bob independently.
By processing type 3 edges first, we maximize their usage, then fill in gaps with type 1 (Alice-only) and type 2 (Bob-only) edges.
3 edges. For each edge, attempt to union in both DSUs. Count it as used if it connects new components in either DSU.1 edges (Alice only) and type 2 edges (Bob only). For each edge, attempt to union in the appropriate DSU and count if successful.total_edges - used_edges. Otherwise, return -1.class DSU:
def __init__(self, n):
self.n = n
self.Parent = list(range(n + 1))
self.Size = [1] * (n + 1)
def find(self, node):
if self.Parent[node] != node:
self.Parent[node] = self.find(self.Parent[node])
return self.Parent[node]
def union(self, u, v):
pu = self.find(u)
pv = self.find(v)
if pu == pv:
return 0
if self.Size[pu] < self.Size[pv]:
pu, pv = pv, pu
self.Size[pu] += self.Size[pv]
self.Parent[pv] = pu
self.n -= 1
return 1
def isConnected(self):
return self.n == 1
class Solution:
def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:
alice, bob = DSU(n), DSU(n)
cnt = 0
for type, src, dst in edges:
if type == 3:
cnt += (alice.union(src, dst) | bob.union(src, dst))
for type, src, dst in edges:
if type == 1:
cnt += alice.union(src, dst)
elif type == 2:
cnt += bob.union(src, dst)
if alice.isConnected() and bob.isConnected():
return len(edges) - cnt
return -1Where is the number of verticies and is the number of edges.
The order of processing matters significantly. Type 3 edges (usable by both Alice and Bob) should be processed first because they provide connectivity to both users with a single edge. If you process type 1 and type 2 edges first, you may use two separate edges to connect nodes that could have been connected by one type 3 edge, resulting in fewer removable edges.
Alice and Bob have different connectivity requirements since type 1 edges only work for Alice and type 2 edges only work for Bob. Using a single DSU structure fails to track their independent connectivity. You must maintain two separate Union-Find structures and update them according to which edge types each user can traverse.
When processing a type 3 edge, it counts as "used" if it connects new components for either Alice or Bob (or both). A common mistake is counting it as used only if it helps both, or counting it twice. Use bitwise OR (|) on the union results: alice.union() | bob.union() returns 1 if the edge was useful for at least one user.
After processing all edges, you must verify that both Alice's and Bob's graphs are fully connected (single component each). Returning the edge count without this check can give a valid-looking answer even when full traversal is impossible. If either DSU has more than one component, return -1.
The Union-Find structure should track the number of connected components, starting at n (number of nodes) and decreasing by 1 with each successful union. A graph is fully connected when exactly 1 component remains. Initializing the count incorrectly or not decrementing on successful unions leads to wrong connectivity checks.