day 16
ticket translation
approach
the input splits into three blocks: rules (each field name with two ranges), your ticket and the nearby tickets. valid_fields lists every field whose ranges contain a number. part 1 sums the numbers on nearby tickets that fit no field at all. a comment mentions an earlier version that merged all the ranges into disjoint intervals, dropped because this is simpler for both parts.
part 2 throws away the invalid tickets and, for each column, intersects the sets of fields that every remaining ticket allows. the columns are then sorted by those candidate sets (python compares sets by inclusion, so the most constrained come first), and each column takes the one field not already used, sudoku style, as the comment puts it. the answer multiplies your ticket's values for the fields starting with departure.
the trick
intersect the possible fields per column, then resolve the most constrained column first and eliminate.
vt = [tt for tt in nt if valid(tt, rs)]
fxc = fields_by_columns(vt, rs)
res, cur = [0] * len(rules), set()
for i, fs in sorted(enumerate(fxc), key = lambda x:x[1]):
res[i] = (fs - cur).pop()
cur |= fs
return prod(t[i] for i,f in enumerate(res) if f.startswith("departure"))