1. MSB-to-LSB Greedy Dominance & Dynamic Deletion via cnt[u]
Why must a Bitwise Trie for XOR maximization or order-statistic comparison be rooted at the Most Significant Bit ($W - 1$) rather than bit $0$? Because binary value order is lexicographical from MSB to LSB: every number in the 1-branch at depth $k$ is strictly greater than every number in the 0-branch, since $2^k > 2^k - 1 = \sum_{j=0}^{k-1} 2^j$.
To support dynamic insert(x) and remove(x) (such as sliding-window XOR or multiset queries), we store an integer counter cnt[u] at every trie node recording how many active numbers pass through node $u$. Instead of physically freeing nodes on remove(x), we simply decrement cnt[u]-- along $x$'s path, and treat any child $c$ with c === 0 || cnt[c] === 0 as empty!
- Maximum XOR Subarray / Tree Path: Since $\bigoplus_{k=L}^{R} A[k] = P[R] \oplus P[L-1]$ where $P[i] = P[i-1] \oplus A[i]$ (with $P[0] = 0$), insert $P[0]$ first, then for each $R = 1 \dots N$, query
maxXor(P[R])and insert $P[R]$. (On a tree, replace $P[i]$ with the root-to-node path XOR!) - Count of $y \in S$ with $(x \oplus y) < K$: At bit $i$, let $b_x = (x \gg i) \mathrel{\&} 1$ and $b_k = (K \gg i) \mathrel{\&} 1$. If $b_k = 1$, then any $y$ in child $b_x$ produces $0 < 1$ at bit $i$ and is guaranteed $< K$, so we add
cnt[child[u][b_x]]in $O(1)$ and continue down child $b_x \oplus 1$ (where bit $i$ of $x \oplus y$ equals $1 = b_k$). If $b_k = 0$, we are forced to descend into child $b_x$. - Range XOR Query $\max_{L \le i \le R} (x \oplus A[i])$ via
maxIdx[u]: Insert $A[0], \dots, A[R]$ in order, updatingmaxIdx[u] = ialong each path. A branch $c$ contains at least one element from $[L, R]$ if and only ifmaxIdx[c] >= L!
{ left, right, cnt }, allocate a single Int32Array(2 * maxNodes) for transitions (ch[(u << 1) | bit]) and Int32Array(maxNodes) for cnt[u]. Because (u << 1) | 0 and (u << 1) | 1 sit in adjacent 4-byte words in the same 64-byte cache line, testing ch[(u << 1) | (b ^ 1)] and falling back to ch[(u << 1) | b] never incurs a second cache miss!
Walk a Bitwise 0/1 Binary Trie greedily taking the opposite bit (1 − b) from MSB to LSB to maximize x ⊕ y, inspect Path-Compressed Radix/PATRICIA edge splits ("romane" / "romanus" / "rubens"), and examine UTF-8 / 24-byte SSO memory layouts.
Invariant & State
2. Production Implementation: Flat-Pool Bitwise Trie (insert, remove, maxXor, countXorLess, maxSubarrayXor)
/**
* High-Performance 30-Bit Binary Trie using a Flat TypedArray Pool
* Supports dynamic insert, remove, maxXor(x), and countXorLess(x, K) in O(W) time.
*/
class BitwiseTrie {
constructor(maxInsertions = 100005, bitWidth = 30) {
this.W = bitWidth;
const maxNodes = maxInsertions * this.W + 2;
this.ch = new Int32Array(maxNodes * 2); // ch[(u << 1) | bit]
this.cnt = new Int32Array(maxNodes);
this.ptr = 1; // Node 1 is root; Node 0 is null sentinel
}
insert(x, delta = 1) {
let u = 1;
this.cnt[u] += delta;
for (let i = this.W - 1; i >= 0; i--) {
const b = (x >>> i) & 1;
const edgeIdx = (u << 1) | b;
let next = this.ch[edgeIdx];
if (next === 0) {
next = ++this.ptr;
this.ch[edgeIdx] = next;
}
u = next;
this.cnt[u] += delta;
}
}
remove(x) {
this.insert(x, -1);
}
maxXor(x) {
let u = 1;
if (this.cnt[u] === 0) return 0;
let ans = 0;
for (let i = this.W - 1; i >= 0; i--) {
const b = (x >>> i) & 1;
const want = b ^ 1;
const desiredChild = this.ch[(u << 1) | want];
if (desiredChild !== 0 && this.cnt[desiredChild] > 0) {
ans |= (1 << i);
u = desiredChild;
} else {
u = this.ch[(u << 1) | b];
}
}
return ans >>> 0;
}
/**
* Returns count of active elements y in the trie such that (x ^ y) < k
*/
countXorLess(x, k) {
let u = 1;
let total = 0;
for (let i = this.W - 1; i >= 0 && u !== 0; i--) {
const bx = (x >>> i) & 1;
const bk = (k >>> i) & 1;
if (bk === 1) {
const sameChild = this.ch[(u << 1) | bx];
if (sameChild !== 0) total += this.cnt[sameChild];
u = this.ch[(u << 1) | (bx ^ 1)];
} else {
u = this.ch[(u << 1) | bx];
}
}
return total;
}
}
/**
* Maximum XOR of any contiguous subarray A[L..R] in O(N * W) time
*/
function maxXorSubarray(arr, bitWidth = 30) {
const trie = new BitwiseTrie(arr.length + 1, bitWidth);
trie.insert(0); // Prefix XOR P[0] = 0 so subarrays starting at index 0 are included!
let pref = 0;
let best = 0;
for (let i = 0; i < arr.length; i++) {
pref ^= arr[i];
best = Math.max(best, trie.maxXor(pref));
trie.insert(pref);
}
return best;
}
3. Complexity Matrix
| Problem ($N$ integers of $W$ bits) | Brute Force | Bitwise (0-1) Trie | Space Complexity |
|---|---|---|---|
| Max XOR Pair / Max XOR Subarray | $\Theta(N^2)$ | $\Theta(N \cdot W)$ | $O(N \cdot W)$ integers |
| Count Subarrays with $\text{XOR}(L..R) < K$ | $\Theta(N^2)$ | $\Theta(N \cdot W)$ | $O(N \cdot W)$ integers |
Offline Subarray $\max_{i \in [L, R]} (x \oplus A[i])$ (maxIdx) |
$O(Q \cdot N)$ | $O((N + Q) \cdot W)$ | $O(N \cdot W)$ integers |
| Online Subarray $\max_{i \in [L, R]} (x \oplus A[i])$ (Persistent 0-1 Trie) | $O(Q \cdot N)$ | $O((N + Q) \cdot W)$ | $O(N \cdot W)$ nodes |
P[0] = 0 Before the Loop in Subarray XOR Problems: Every subarray XOR $\bigoplus_{k=L}^{R} A[k]$ equals $P[R] \oplus P[L-1]$. When $L = 1$ (a subarray that starts at the very first element of the array), the required partner is $P[0] = 0$. If you forget trie.insert(0) before iterating over $R = 1 \dots N$, your algorithm will never consider subarrays starting at index $0$ (unless some later prefix happens to XOR to $0$)!