You are given a region table and a list of party attendance intervals for towns within a single day. For each town, first compute the union of its party intervals inside the day window [day_start, day_end). Then compute that town's idle segments: the time ranges in the day when the town is not at a party. Finally, group those idle segments by city. Return all idle segments per town, grouped under each city_id. Intervals may be unsorted, overlapping, touching, or partially outside the day; clamp them to the day window before processing. If a town has no valid party intervals, its idle segment is the full day. If a town is at a party for the entire day, it contributes no idle segment. Include every city from region_data in the result, even if its list is empty.
Examples
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North'), ('t3', 'Elm', 'c2', 'Lake', 's1', 'North')], [('t1', 60, 120), ('t1', 90, 180), ('t2', 300, 360), ('t3', 0, 600)], 0, 600)
Expected Output: {'c1': [('t1', 0, 60), ('t1', 180, 600), ('t2', 0, 300), ('t2', 360, 600)], 'c2': []}
Explanation: t1 has idle [0,60] and [180,600]. t2 has idle [0,300] and [360,600]. t3 is busy for the full day window, so c2 gets an empty list.
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North'), ('t3', 'Elm', 'c2', 'Lake', 's1', 'North')], [('t1', -30, 30), ('t1', 90, 150), ('t3', 50, 70), ('t3', 70, 80)], 0, 120)
Expected Output: {'c1': [('t1', 30, 90), ('t2', 0, 120)], 'c2': [('t3', 0, 50), ('t3', 80, 120)]}
Explanation: t1 becomes busy on [0,30] and [90,120], leaving [30,90]. t2 has no intervals, so it is idle the whole day. t3's touching intervals merge to [50,80].
Input: ([('t1', 'Solo', 'c1', 'Metro', 's1', 'North')], [], 10, 20)
Expected Output: {'c1': [('t1', 10, 20)]}
Explanation: With no party intervals, the town is idle for the entire day window.
Input: ([('t1', 'Oak', 'c1', 'Metro', 's1', 'North'), ('t2', 'Pine', 'c1', 'Metro', 's1', 'North')], [('t1', 10, 20), ('t1', 20, 30), ('t1', 25, 40), ('t2', 0, 100)], 0, 100)
Expected Output: {'c1': [('t1', 0, 10), ('t1', 40, 100)]}
Explanation: t1's touching and overlapping intervals merge to [10,40], so there is no gap at 20. t2 covers the whole day and contributes no idle segment.
Solution
def solution(region_data, party_intervals, day_start, day_end):
town_to_city = {}
town_order = []
result = {}
for row in region_data:
town_id, _, city_id, _, _, _ = row
if city_id not in result:
result[city_id] = []
if town_id not in town_to_city:
town_to_city[town_id] = city_id
town_order.append(town_id)
grouped = {town_id: [] for town_id in town_order}
for town_id, start, end in party_intervals:
if town_id not in grouped:
continue
start = max(start, day_start)
end = min(end, day_end)
if start < end:
grouped[town_id].append((start, end))
for town_id in town_order:
intervals = sorted(grouped[town_id])
merged = []
for start, end in intervals:
if not merged or start > merged[-1][1]:
merged.append([start, end])
else:
if end > merged[-1][1]:
merged[-1][1] = end
city_id = town_to_city[town_id]
prev = day_start
for start, end in merged:
if prev < start:
result[city_id].append((town_id, prev, start))
prev = end
if prev < day_end:
result[city_id].append((town_id, prev, day_end))
return resultTime complexity: O(R + P log P), where R is the number of region rows and P is the number of party intervals.. Space complexity: O(R + P).