Java  

Construct List Using XOR Queries – Efficient Reverse Processing Approach

Introduction

The Construct List Using XOR Queries problem is a clever application of bit manipulation and reverse traversal. At first glance, the problem appears to require updating every element in the array whenever an XOR query is encountered. However, doing so would result in an inefficient solution that cannot handle the given constraints.

The key to solving this problem efficiently lies in understanding how XOR operations behave and using that property to avoid repeatedly modifying the entire array.

Problem Overview

The array initially contains a single element:

[0]

We are given two types of queries:

  • 0 x → Insert x into the array.

  • 1 x → Replace every element a in the array with a ^ x.

After executing all queries, we must return the final array in sorted order.

Example

queries = [
    [0, 6],
    [0, 3],
    [0, 2],
    [1, 4],
    [1, 5]
]

The array evolves as follows:

[0]
[0, 6]
[0, 6, 3]
[0, 6, 3, 2]
[4, 2, 7, 6]
[1, 7, 2, 3]

After sorting:

[1, 2, 3, 7]

The Naive Approach

A straightforward approach would be to maintain the array and, for every XOR query, iterate through all elements and update them.

Although simple, this approach is extremely inefficient.

Imagine there are 100,000 queries. If many of them are XOR operations, repeatedly traversing the entire array could lead to a time complexity close to O(q²), which exceeds the allowed limits.

To optimize the solution, we need to observe an important property of XOR.

Key Observation: XOR Properties

XOR is both associative and commutative:

(a ^ b) ^ c = a ^ (b ^ c)

This means multiple XOR operations can be combined into a single cumulative XOR value.

For example:

a ^ 4 ^ 5

can be written as:

a ^ (4 ^ 5)

Instead of applying XOR to every element immediately, we can simply remember the cumulative XOR value.

The Reverse Traversal Insight

The real trick comes from processing the queries in reverse order.

Suppose we traverse from the last query toward the first.

Whenever we encounter:

1 x

we update a running XOR variable:

xor ^= x;

This variable represents all XOR operations that will affect elements inserted before this point.

When we encounter:

0 x

the value x was inserted before all XOR operations currently stored in xor.

Therefore, its final value will be:

x ^ xor

We can directly store this transformed value in our answer list.

Why Reverse Traversal Works

Every inserted element is affected only by XOR operations that occur after its insertion.

While traversing in reverse:

  • We have already processed all future XOR operations.

  • The cumulative XOR contains exactly the XORs that will affect the current inserted value.

  • Therefore, x ^ xor directly gives the element's final value.

This eliminates the need to repeatedly update previously inserted elements.

Step-by-Step Example

Consider the earlier example:

[0, 6]
[0, 3]
[0, 2]
[1, 4]
[1, 5]

Start with:

xor = 0

Process from the end:

[1, 5] → xor = 5
[1, 4] → xor = 5 ^ 4 = 1
[0, 2] → store 2 ^ 1 = 3
[0, 3] → store 3 ^ 1 = 2
[0, 6] → store 6 ^ 1 = 7

Add the initial element:

0 ^ 1 = 1

which is simply:

xor

Collected values:

[3, 2, 7, 1]

After sorting:

[1, 2, 3, 7]

This produces the correct answer.

Algorithm

The algorithm can be summarized as follows:

  1. Initialize a variable xor = 0.

  2. Traverse the queries from right to left.

  3. For every XOR query, update the cumulative XOR.

  4. For every insertion query, store value ^ xor.

  5. Add the transformed initial element (xor).

  6. Sort the resulting list.

  7. Return the sorted list.

Java Implementation

class Solution {
    public ArrayList<Integer> constructList(int[][] queries) {
        ArrayList<Integer> ans = new ArrayList<>();

        int xor = 0;

        for (int i = queries.length - 1; i >= 0; i--) {
            int type = queries[i][0];
            int x = queries[i][1];

            if (type == 1) {
                xor ^= x;
            } else {
                ans.add(x ^ xor);
            }
        }

        ans.add(xor);

        Collections.sort(ans);

        return ans;
    }
}

Complexity Analysis

Let:

  • q = number of queries

Time Complexity

O(q log q)

Reason:

  • Reverse traversal takes O(q) time.

  • Sorting the final list dominates the runtime and requires O(q log q) time.

Space Complexity

O(q)

The answer list stores all inserted values and the initial element.

Why This Solution Is Efficient

The reverse-processing technique transforms what appears to be a costly array-update problem into an efficient bit-manipulation problem.

Instead of:

  • Updating every element for each XOR query,

we:

  • Maintain a single cumulative XOR value.

  • Compute each element's final value only once.

This reduces the processing cost from potentially O(q²) to O(q) before sorting.

Key Takeaway

The core insight is that XOR operations can be merged into a single cumulative value because XOR is associative and commutative.

By traversing the queries in reverse:

  • Future XOR operations are accumulated in a single variable.

  • Every inserted element can be transformed directly into its final value.

  • No repeated array updates are required.

This elegant combination of reverse traversal and cumulative XOR converts a seemingly expensive simulation problem into an efficient O(q log q) solution that easily handles up to 100,000 queries.

Summary

The Construct List Using XOR Queries problem can be solved efficiently by exploiting the associative and commutative properties of XOR. Instead of applying every XOR operation to the entire array, we process the queries in reverse while maintaining a cumulative XOR value. Each inserted element is transformed directly into its final value using value ^ xor, and the initial element contributes xor itself. After collecting all values, sorting the result produces the required final array. This approach reduces the problem from a potentially quadratic simulation to an efficient solution with O(q log q) time complexity and O(q) space complexity.