Maximum XOR With an Element From Array
Each query (x, m) asks: max x XOR a over array elements a ≤ m (or −1 if none). Answer all queries fast.
Intuition
This is problem 421 with a ceiling. Handle it offline: sort the array, and sort queries by their limit m. Sweep queries in increasing m, inserting array elements into the bit-trie only while they are ≤ m. Each query then greedily walks a trie that contains exactly its allowed elements — the constraint dissolves into insertion order.
Offline processing: sort the queries by their limit and the array by value, then a single pointer admits elements into the trie as the limit grows. Each element is inserted once across all queries. Reordering queries when they arrive in an inconvenient order is the general technique.
Approach
Constraint blocks direct reuse
The 421 trie answers 'max XOR with anything'. The a ≤ m filter differs per query, so a static trie over-answers. Rebuilding per query is O(n) each — too slow.
Offline sweep
Sort nums ascending; sort queries by m (remembering original indices). A pointer inserts nums[ptr] while nums[ptr] ≤ m. Trie contents = exactly the eligible elements — no removal ever needed.
Greedy query as before
Walk 30 bits top-down preferring the opposite bit. Empty trie (m below the minimum element) → −1.
Solution & live demo
Common pitfalls
Rebuilding the trie per query
for m, x, i in queries:
trie = build([v for v in nums if v <= m])while ptr < len(nums) and nums[ptr] <= m:
# insert into the shared trieThat's O(q · n · bits). Because the sorted limits only increase, the set of eligible elements only grows — so one shared trie and a monotone pointer insert each element exactly once.
Losing the original query order
return [answer for each sorted query]
qs = sorted([(m, x, i) for i, (x, m) in enumerate(queries)]) ... ans[i] = val
The output must line up with the input order, but processing happens in limit order. Carrying the original index through the sort and writing to ans[i] restores it.
Not handling an empty trie
node, val = root, 0 for b in range(29, -1, -1):
if not root: continue
When no element is at or below the limit, the answer is −1. Walking an empty trie dereferences missing children and either throws or returns a fabricated 0.
Edge cases
Nothing inserted yet — answer −1.
Insert both; identical paths are harmless.
Carry each query's original index through the sort.