Treat integers as vectors over (bit is coordinate ). XOR is vector addition, so a set of numbers spans a linear subspace, and Gaussian elimination gives a basis of at most elements (30 or 60 bits).

The structure

struct XorBasis {
    static const int B = 60;
    long long basis[B] = {};
    int sz = 0;
 
    bool insert(long long x) {                 // returns true if x expanded the span
        for (int i = B - 1; i >= 0; i--) {
            if (!(x >> i & 1)) continue;
            if (!basis[i]) { basis[i] = x; sz++; return true; }
            x ^= basis[i];
        }
        return false;                          // x was already representable
    }
 
    bool contains(long long x) const {
        for (int i = B - 1; i >= 0; i--)
            if (x >> i & 1) { if (!basis[i]) return false; x ^= basis[i]; }
        return x == 0;
    }
 
    long long maxXor(long long start = 0) const {
        long long res = start;
        for (int i = B - 1; i >= 0; i--)
            if (basis[i] && (res ^ basis[i]) > res) res ^= basis[i];
        return res;
    }
};

basis[i] holds a vector whose highest set bit is — the reduced row echelon form, maintained incrementally.

Each insertion is , so building from numbers is .

What it answers

QuestionMethod
Maximum XOR of any subsetgreedy from the top bit
Minimum non-zero XOR of a subsetthe smallest basis element after full reduction
Is representable as a subset XOR?contains(x)
Number of distinct subset XORs
Number of subsets giving a specific XOR if representable, else 0
-th smallest subset XORfully reduce the basis, then read in binary
Rank of the set over sz
Merge two setsinsert one basis into the other,

The fact is the one people forget: every achievable XOR value is achieved by exactly subsets, because the kernel of the map has that size.

-th smallest subset XOR

Fully reduce so each basis vector’s leading bit appears in no other vector:

void reduce() {
    for (int i = 0; i < B; i++)
        if (basis[i])
            for (int j = i + 1; j < B; j++)
                if (basis[j] >> i & 1) basis[j] ^= basis[i];
}
long long kth(long long k) {                   // 0-indexed among the 2^sz values
    reduce();
    long long res = 0;
    int idx = 0;
    for (int i = 0; i < B; i++)
        if (basis[i]) { if (k >> idx & 1) res ^= basis[i]; idx++; }
    return res;
}

After reduction, the basis vectors are independent in each bit position, so the -th value is read directly from ‘s binary representation.

Basis vs binary trie

XOR basisBinary trie
Representsthe span of the setthe elements of the set
Max XOR over subsets
Max XOR with a single element
Count elements with a property
Deletionhard
Memory
Merge two structures

Read the problem carefully: “choose a subset” → basis; “choose an element” → trie.

Range queries on a basis

Prefix bases. Maintain, for each prefix, a basis where each vector also records the latest index at which it could be inserted. Then a query on uses the prefix- basis restricted to vectors with index :

long long b[B], pos[B];
void insert(long long x, int idx) {
    for (int i = B-1; i >= 0; i--) {
        if (!(x >> i & 1)) continue;
        if (!b[i]) { b[i] = x; pos[i] = idx; return; }
        if (pos[i] < idx) { swap(b[i], x); swap(pos[i], idx); }   // keep the newer one
        x ^= b[i];
    }
}
// query max XOR over [l, r]: use only basis vectors with pos >= l

preprocessing, per query. This is the standard solution to “maximum subset XOR in a range”.

Merging on a tree. Bases merge in , which makes them usable with small-to-large, segment trees over ranges, and binary lifting on trees (maximum XOR on a tree path).

Cycle space of a graph

The XOR of edge weights around any cycle forms a linear space. Build a spanning tree, and for each non-tree edge insert dist[u] ^ dist[v] ^ w into a basis. Then:

  • Maximum XOR path from to = maxXor(dist[u] ^ dist[v]).
  • The number of distinct path XOR values between two vertices is .

This is a beautiful application: a graph problem reduced entirely to linear algebra over .

See also: Linear Basis · Binary Trie · Gaussian Elimination