Linear Basis

Recalling the concept of basis vectors from high-school solid geometry, we can find a set of basis vectors i\boldsymbol{i}, j\boldsymbol{j}, and k\boldsymbol{k} in three-dimensional Euclidean space, after which any vector in the space can be represented by these basis vectors. In other words, we can describe an infinite three-dimensional space using finitely many basis vectors, which is enough to demonstrate the importance of basis vectors.

Three-dimensional Euclidean space is a special linear space, and its basis vectors are generalized to linear bases in linear spaces.

Applications of linear bases in OI usually involve only two kinds of linear spaces: the nn-dimensional real linear space Rn\mathbf{R}^n and the nn-dimensional linear space Z2n\mathbf{Z}_2^n over the Boolean domain. We will introduce them in detail in the Applications section. If you are unfamiliar with linear algebra, we recommend starting with the Applications section.

The following discussion introduces linear bases from the perspective of general linear spaces and gives their common properties.

Prerequisite: Linear spaces.

A linear basis is a basis of a linear space and an important tool for studying linear spaces.

Definition

An inclusion-maximal linearly independent set in a linear space VV is called a Hamel basis or linear basis of VV, abbreviated as a basis.

The basis of the linear space {θ}\{\theta\} is defined to be the empty set.

It can be proved that every linear space has a linear basis1. We define the dimension of a linear space VV as the number of elements (or the cardinality) of a linear basis, denoted by dim⁡V\dim V.

Properties

  1. For a finite-dimensional linear space VV of dimension nn:

    1. In VV, any collection of n+1n+1 vectors is linearly dependent.

    2. In VV, any nn linearly independent vectors form a basis of VV.

    3. If every vector in VV can be expressed linearly by the vector group a1,a2,…,ana_1,a_2,\dots,a_n, then it is a basis of VV.

      ???+ note "Proof" In VV, take any basis b1,b2,…,bnb_1,b_2,\dots,b_n. By the given condition, the vector group b1,b2,…,bnb_1,b_2,\dots,b_n can be expressed linearly by a1,a2,…,ana_1,a_2,\dots,a_n, so

      $$
      n=\operatorname{rank}\{b_1,b_2,\dots,b_n\}\leq\operatorname{rank}\{a_1,a_2,\dots,a_n\}\leq n
      $$
      
      Therefore, $\operatorname{rank}\{a_1,a_2,\dots,a_n\}=n$.
      
    4. In VV, any linearly independent vector group a1,a2,…,ama_1,a_2,\dots,a_m can be made into a basis of VV by inserting some vectors.

  2. (The subspace dimension formula.) Let V1,V2V_1,V_2 be finite-dimensional linear spaces over P\Bbb{P}, and suppose that V1+V2V_1+V_2 and V1∩V2V_1\cap V_2 are also finite-dimensional. Then dim⁡V1+dim⁡V2=dim⁡(V1+V2)+dim⁡(V1∩V2)\dim V_1+\dim V_2=\dim(V_1+V_2)+\dim(V_1\cap V_2).

    ???+ note "Proof" Let dim⁡V1=n1\dim V_1=n_1, dim⁡V2=n2\dim V_2=n_2, and dim⁡(V1∩V2)=m\dim(V_1\cap V_2)=m.

    Take a basis of $V_1\cap V_2$, denoted by $a_1,a_2,\dots,a_m$, and extend it to bases of $V_1$ and $V_2$, respectively: $a_1,a_2,\dots,a_m,b_1,b_2,\dots,b_{n_1-m}$ and $a_1,a_2,\dots,a_m,c_1,c_2,\dots,c_{n_2-m}$.
    
    It remains only to prove that the vector group $a_1,a_2,\dots,a_m,b_1,b_2,\dots,b_{n_1-m},c_1,c_2,\dots,c_{n_2-m}$ is linearly independent.
    
    Let $\sum_{i=1}^m r_ia_i+\sum_{i=1}^{n_1-m} s_ib_i+\sum_{i=1}^{n_2-m} t_ic_i=\theta$.
    
    Then $\sum_{i=1}^{n_2-m} t_ic_i=-\sum_{i=1}^m r_ia_i-\sum_{i=1}^{n_1-m} s_ib_i$.
    
    The left side lies in $V_2$ and the right side lies in $V_1$, so both sides lie in $V_1\cap V_2$. Therefore, $\sum_{i=1}^{n_2-m} t_ic_i=\sum_{i=1}^m k_ia_i$.
    
    Hence $t_1=t_2=\dots=t_{n_2-m}=k_1=k_2=\dots=k_m=0$, and consequently $r_1=r_2=\dots=r_m=s_1=s_2=\dots=s_{n_1-m}=t_1=t_2=\dots=t_{n_2-m}=0$.
    
  3. Let V1,V2V_1,V_2 be finite-dimensional linear spaces over P\Bbb{P}, and suppose that V1+V2V_1+V_2 and V1∩V2V_1\cap V_2 are also finite-dimensional. The following statements are equivalent:

    1. V1+V2=V1⊕V2V_1+V_2=V_1\oplus V_2.

    2. dim⁡V1+dim⁡V2=dim⁡(V1+V2)\dim V_1+\dim V_2=\dim(V_1+V_2).

    3. If a1,a2,…,ana_1,a_2,\dots,a_n is a basis of V1V_1 and b1,b2,…,bmb_1,b_2,\dots,b_m is a basis of V2V_2, then a1,a2,…,an,b1,b2,…,bma_1,a_2,\dots,a_n,b_1,b_2,\dots,b_m is a basis of V1+V2V_1+V_2.

    ???+ note "Note" Statements 1 and 3 can be generalized to infinite-dimensional linear spaces.

Examples

Consider bases of R2\Bbb{R}^2.

  1. As shown in the figure:

    Figure: basis 1

    u,vu,v form a basis.

  2. As shown in the figure:

    Figure: basis 2

    u,vu,v form a basis.

  3. As shown in the figure:

    Figure: basis 3

    u,vu,v do not form a basis because u=−vu=-v.

  4. As shown in the figure:

    Figure: basis 4

    u,v,wu,v,w do not form a basis because u+4v+6w=θu+4v+6w=\theta.

Orthogonal Bases and Orthonormal Bases

If a linear space VV has a basis BB satisfying ∀b,b′∈B, (b,b′)≠0  ⟺  b=b′\forall b,b'\in B,~(b,b')\ne 0\iff b=b' (that is, its elements are pairwise orthogonal), then this basis is called an orthogonal basis.

If a linear space VV has an orthogonal basis BB that also satisfies ∀b∈B, ∣b∣=(b,b)=1\forall b\in B,~|b|=\sqrt{(b,b)}=1, this basis is called an orthonormal basis.

The basis of any finite-dimensional linear space VV can be transformed into an orthogonal basis through Schmidt orthogonalization.

Applications

Based on the preceding discussion, linear bases can be used to:

  1. Find the rank of a given vector group;
  2. Find a maximal linearly independent set (or a basis of its span) for a given vector group;
  3. Insert some vectors into a given vector group and find a maximal linearly independent set (or a basis of its span) in the resulting vector group;
  4. Determine whether a vector can be expressed linearly by a maximal linearly independent set (or basis) that has been found;
  5. Find special elements in the span of a maximal linearly independent set (or basis) that has been found, such as the maximum or minimum element.

In OI, a linear basis of the nn-dimensional real linear space Rn\mathbf{R}^n is generally called a real linear basis, while a linear basis of the nn-dimensional Boolean-domain linear space Z2n\mathbf{Z}_2^n is called an XOR linear basis.

Tip

Addition in Z2\mathbf{Z}_2 is XOR and multiplication is AND; one can prove that Z2\mathbf{Z}_2 is a field.

One can prove that the algebraic system (Z2n,+,⋅,Z2)(\mathbf{Z}_2^n,+,\cdot,\mathbf{Z}_2) is a linear space, where:

(a1,…,an)+(b1,…,bn):=(a1+b1,…,an+bn),(a_1,\dots,a_n)+(b_1,\dots,b_n):=(a_1+b_1,\dots,a_n+b_n), k⋅(a1,…,an):=(ka1,…,kan).k\cdot(a_1,\dots,a_n):=(ka_1,\dots,ka_n).

That is, addition is XOR and scalar multiplication is AND.

Taking an XOR linear basis as an example, for a given set of Boolean sequences X={x1,…,xm}X=\{x_1,\dots,x_m\}, we can construct an XOR linear basis B={b1,…,bn}B=\{b_1,\dots,b_n\} with the following properties:

  1. The XOR sum of any nonempty subset of BB is not 00;
  2. For XX, any element xx can be obtained by taking some elements from BB whose XOR sum is xx;
  3. For any set B′B' satisfying the preceding two conditions, the number of its elements is no smaller than the number of elements in BB.

An XOR linear basis can be used to:

  1. Determine whether a number can be represented as the XOR sum of a subset of a given set of numbers;
  2. Count the ways to represent a number as the XOR sum of a subset of a given set of numbers;
  3. Find the maximum, minimum, kk-th largest, or kk-th smallest XOR sum of a subset of a given set of numbers;
  4. Find the rank of a number among the XOR sums of subsets of a given set of numbers.

Construction

Since XOR linear bases and real linear bases have no essential difference, we use XOR linear bases as the example below; the code for a real linear basis only requires minor modifications.

Greedy Method

Convert each number pp in the original set to binary and scan from the highest bit to the lowest. If bit xx is 11 and axa_x does not exist, set ax←pa_x \leftarrow p and end the scan; if it exists, set p←p xor axp\leftarrow p~\text{xor}~a_x.

To query the maximum value of xor\text{xor} over any number of elements from the original set, scan the linear basis from the highest bit to the lowest. If applying the current xor\text{xor} operation to axa_x makes the answer larger, XOR the answer with axa_x.

Why does this work? When scanning from high to low bits, if we have reached bit ii, we can ensure that bit ii of the answer is 11, and no later operation can change bit ii.

The minimum value of xor\text{xor} over any number of elements from the original set is simply the smallest element in the linear-basis set.

To query whether a number can be obtained by XOR, proceed similarly to insertion: if the number pp left at the end of insertion has been XORed to 00, it can be obtained.

Code (Luogu P3812 [Template] Linear Basis)
#include <algorithm>
#include <iostream>
using ull = unsigned long long;

ull p[64];

void insert(ull x) {
  for (int i = 63; ~i; --i) {
    if (!(x >> i))  // Bit i of x is 0
      continue;
    if (!p[i]) {
      p[i] = x;
      break;
    }
    x ^= p[i];
  }
}

using std::cin;
using std::cout;

int main() {
  int n;
  cin >> n;
  ull a;
  for (int i = 1; i <= n; ++i) {
    cin >> a;
    insert(a);
  }
  ull ans = 0;
  for (int i = 63; ~i; --i) {
    ans = std::max(ans, ans ^ p[i]);
  }
  cout << ans << '\n';
  return 0;
}

Gaussian Elimination

Gaussian elimination constructs a linear basis from the perspective of a system of linear equations; its correctness is clear.

Code (Luogu P3812 [Template] Linear Basis)
#include <iostream>
using ull = unsigned long long;
constexpr int MAXN = 1e5 + 5;

ull deg(ull num, int deg) { return num & (1ull << deg); }

ull a[MAXN];
using std::cin;
using std::cout;

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  int n;
  cin >> n;
  for (int i = 1; i <= n; ++i) cin >> a[i];
  int row = 1;
  for (int col = 63; ~col && row <= n; --col) {
    for (int i = row; i <= n; ++i) {
      if (deg(a[i], col)) {
        std::swap(a[row], a[i]);
        break;
      }
    }
    if (!deg(a[row], col)) continue;
    for (int i = 1; i <= n; ++i) {
      if (i == row) continue;
      if (deg(a[i], col)) {
        a[i] ^= a[row];
      }
    }
    ++row;
  }
  ull ans = 0;
  for (int i = 1; i < row; ++i) {
    ans ^= a[i];
  }
  cout << ans << '\n';
  return 0;
}

Properties

The linear basis constructed by the greedy method has the following properties:

  • No subset of the linear basis has XOR sum 00.
  • The highest set bit of each number in the linear basis is different.

The linear basis constructed by Gaussian elimination satisfies the following property:

  • The matrix after Gaussian elimination is a row-reduced echelon matrix.

    This property includes the two properties satisfied by the linear basis constructed by the greedy method.

    If you do not understand why this property holds, you can read Gaussian elimination.

Here is an example:

5
633 211 169 841 1008

Binary representations:

1001111001
0011010011
0010101001
1101001001
1111110000

Linear basis generated by the greedy method:

1001111001
0100110000
0011010011
0001111010
0000000000
0000010000
0000000000
0000000000
0000000000
0000000000

Linear basis generated by Gaussian elimination:

1000000011
0100100000
0010101001
0001101010
0000010000
0000000000
0000000000
0000000000
0000000000
0000000000

This is a very useful property that makes many problems easier to solve. For example, given some numbers, if we want the maximum XOR of a subset and construct the linear basis greedily, we need to perform another greedy pass: if the current bit of ans is 0, XORing it will improve the answer; if the current bit is 1, it will not. After constructing the basis by Gaussian elimination, we can simply XOR all basis elements and output the result.

For other classic problems, such as querying whether a number can be obtained by XOR or finding the kk-th largest XOR value, the basis obtained by Gaussian elimination is also more convenient.

Time Complexity

Let the vector length be nn and the total number of vectors be mm. The time complexity is O(nm)O(nm). The constant factor of Gaussian elimination is slightly larger.

For a real linear basis, the time complexity is O(n2m)O(n^2m).

Merging Linear Bases

Merging linear bases only requires brute force: insert every vector from one linear basis into the other. The time complexity of one merge is O(n2)O(n^2) for an XOR linear basis or O(n3)O(n^3) for a real linear basis.

Intersecting Linear Bases

Strictly speaking, intersecting linear bases means finding a linear basis of the intersection of the two linear spaces that they span. This section presents two algorithms. For both algorithms, the time complexity of one intersection is O(n2)O(n^2) for an XOR linear basis or O(n3)O(n^3) for a real linear basis.

Naive Algorithm

Let the two linear bases to be intersected be α\alpha and β\beta. The intersection algorithm only needs the following adjustments to the brute-force linear-basis merge algorithm (using an XOR linear basis as an example):

  • Using the linear basis β\beta, try to insert its vector βj\beta_j into α\alpha using the greedy method, and initialize the intersection basis γ\gamma as the empty set;
  • During insertion, record the contribution of elements from the linear basis β\beta to the vector being inserted. Specifically, maintain a new vector bb, initialized to βj\beta_j; if the vector being inserted is XORed with the vector at position xx in the linear basis, XOR the contribution bb with the contribution recorded at position xx, namely bxb_x;
  • If insertion succeeds and at position xx the vector βj′\beta_j' is inserted, set the recorded value at position xx, namely bxb_x, to the contribution that produced βj′\beta_j' from elements of β\beta, namely bb;
  • If insertion fails, insert the contribution of the elements of β\beta, recorded as bb, into γ\gamma.

The resulting linear basis γ\gamma is the desired intersection; the algorithm also computes the union of the two linear bases.

Explanation of the Algorithm

Let the merged linear basis be {α1,⋯ ,αm,βj1′,⋯ ,βjℓ′}\{\alpha_1,\cdots,\alpha_m,\beta'_{j_1},\cdots,\beta'_{j_\ell}\}, where βjk′\beta'_{j_k} is the vector obtained after inserting βjk\beta_{j_k}. Then {α1,⋯ ,αm,βj1,⋯ ,βjℓ}\{\alpha_1,\cdots,\alpha_m,\beta_{j_1},\cdots,\beta_{j_\ell}\} is also a merged linear basis. Let β+\beta^+ be the set {βj1,⋯ ,βjℓ}\{\beta_{j_1},\cdots,\beta_{j_\ell}\}. The merged basis can then be written as α∪β+\alpha\cup\beta^+. Moreover, every vector cc in the sum space has a unique representation of the form

c=a⊕bc = a\oplus b

where a∈span⁡αa\in\operatorname{span}\alpha and b∈span⁡β+b\in\operatorname{span}\beta^+. The bb in this decomposition is the “contribution of elements of the linear basis β\beta” that the preceding algorithm attempts to record. Strictly speaking, it is the contribution of the vectors in β\beta that were inserted successfully at the end.

For a successful insertion, the final recorded bb is the bb term in this decomposition. Let βj∈β+\beta_j\in\beta^+. Initially, βj=0⊕βj\beta_j=0\oplus\beta_j, which is already the correct decomposition of βj\beta_j in the basis α∪β+\alpha\cup\beta^+. When updating βj′=a⊕b\beta'_j=a\oplus b to βj′⊕cx\beta'_j\oplus c_x, since βj′⊕cx=(a⊕ax)⊕(b⊕bx)\beta_j'\oplus c_x=(a\oplus a_x)\oplus(b\oplus b_x), it is enough to update bb to b⊕bxb\oplus b_x to preserve the decomposition. Therefore, by induction, when βj′\beta'_j is finally inserted into the merged linear basis, the recorded contribution bb is the bb term in the decomposition above.

For an unsuccessful insertion, the vector being inserted will eventually become 00, and its contribution bb is then inserted into γ\gamma. Repeating the preceding argument shows that during insertion we still always have βj′=a⊕b\beta_j'=a\oplus b, with a∈span⁡αa\in\operatorname{span}\alpha, but bb no longer belongs to span⁡β+\operatorname{span}\beta^+. This is because, initially, βj=0⊕βj\beta_j=0\oplus\beta_j, with βj∉β+\beta_j\notin\beta^+. Apart from that, every term XORed into the contribution during updates belongs to span⁡β+\operatorname{span}\beta^+. Thus, in fact, b⊕βj∈span⁡β+b\oplus\beta_j\in\operatorname{span}\beta^+.

Why does inserting all the bb values from unsuccessful insertions into γ\gamma produce a linear basis of the intersection space? First, if insertion of βj\beta_j fails, the final result is 0=a⊕b0=a\oplus b, where a∈span⁡αa\in\operatorname{span}\alpha and b∈span⁡(β+∪{βj})⊆span⁡βb\in\operatorname{span}(\beta^+\cup\{\beta_j\})\subseteq\operatorname{span}\beta. Therefore, b=ab=a must lie in the intersection space span⁡α∩span⁡β\operatorname{span}\alpha\cap\operatorname{span}\beta. Conversely, let cc be any element of the intersection space. Since c∈span⁡βc\in\operatorname{span}\beta, cc can be represented as a linear combination (XOR sum) of elements of β\beta:

c=⨁βj∈βλjβj,c = \bigoplus_{\beta_j\in\beta}\lambda_j\beta_j,

where λj∈{0,1}\lambda_j\in\{0,1\}. For each βj∉β+\beta_j\notin\beta^+, record the corresponding contribution inserted into γ\gamma as bjb_j. Then

c⊕⨁βj∉β+λjbj=⨁βj∈β+λjβj+⨁βj∉β+λj(βj⊕bj),c\oplus\bigoplus_{\beta_j\notin\beta^+}\lambda_jb_j = \bigoplus_{\beta_j\in\beta^+}\lambda_j\beta_j+\bigoplus_{\beta_j\notin\beta^+}\lambda_j(\beta_j\oplus b_j),

Note that bjb_j and cc both lie in the intersection space, so the left side also lies in the intersection space and can therefore be written as a linear combination of elements of α\alpha. At the same time, every term on the right either has βj∈β+\beta_j\in\beta^+, or has βj∉β+\beta_j\notin\beta^+ and βj⊕bj∈β+\beta_j\oplus b_j\in\beta^+. Thus, the right side is a linear combination of elements of β+\beta^+. Since α∪β+\alpha\cup\beta^+ is linearly independent, all coefficients are 00; that is, c=⨁βj∉β+λjbj∈span⁡{b1,⋯ ,bj}c=\bigoplus_{\beta_j\notin\beta^+}\lambda_jb_j\in\operatorname{span}\{b_1,\cdots,b_j\}. This shows that the contributions bb of the vectors that could not be inserted together span the intersection space.

According to this explanation, maintaining the contribution bb during the process is actually maintaining the decomposition a⊕ba\oplus b; moreover, when the contribution is finally inserted into γ\gamma, we always have a=ba=b. Thus, whether we maintain the contributions of elements of α\alpha or of β\beta (that is, whether we maintain aa or bb), the result is correct. To maintain the contributions of elements of α\alpha, only the initialization needs to change: each vector in α\alpha, denoted αi\alpha_i, initially has contribution αi\alpha_i, while an inserted βj\beta_j initially has contribution 00.

The template-problem code is as follows:

Code (Library Checker Intersection of F2\mathbf F_2 vector spaces)
#include <array>
#include <iostream>

class LinearBasis {
  static constexpr int K = 30;
  std::array<int, K> a;

  // Size of basis.
  int size() const {
    int res = 0;
    for (auto x : a) {
      if (x) {
        ++res;
      }
    }
    return res;
  }

 public:
  LinearBasis() : a{} {}

  // Insert vector x.
  void insert(int x) {
    for (int k = K - 1; ~k && x; --k) {
      if ((x >> k) & 1) {
        if (!a[k]) {
          a[k] = x;
        }
        x ^= a[k];
      }
    }
  }

  // Return a basis for *THIS intersecting RHS.
  LinearBasis intersect(const LinearBasis& rhs) const {
    LinearBasis res;
    std::array<int, K> c = a, b_parts = {};
    for (int i = K - 1; ~i; --i) {
      int x = rhs.a[i], b_part = x;
      for (int k = i; ~k && x; --k) {
        if ((x >> k) & 1) {
          if (!c[k]) {
            c[k] = x;
            b_parts[k] = b_part;
          }
          x ^= c[k];
          b_part ^= b_parts[k];
        }
      }
      res.insert(b_part);
    }
    return res;
  }

  // Output.
  void print() const {
    std::cout << size();
    for (auto x : a) {
      if (x) {
        std::cout << ' ' << x;
      }
    }
    std::cout << '\n';
  }
};

int main() {
  int t;
  std::cin >> t;
  for (; t; --t) {
    LinearBasis a, b;
    int n;
    std::cin >> n;
    for (; n; --n) {
      int x;
      std::cin >> x;
      a.insert(x);
    }
    int m;
    std::cin >> m;
    for (; m; --m) {
      int x;
      std::cin >> x;
      b.insert(x);
    }
    a.intersect(b).print();
  }
  return 0;
}

Zassenhaus Algorithm

Another equivalent approach is the Zassenhaus algorithm, which can also compute the union and intersection of two linear bases simultaneously. Its complexity is exactly the same as above.

The steps are as follows:

  • Initialize an empty linear basis of vector length 2n2n, denoted γ\gamma, whose vectors have the form (a,b)(a,b) with both aa and bb of length nn;
  • From α\alpha, insert each element αi\alpha_i in the form (αi,αi)(\alpha_i,\alpha_i) into γ\gamma;
  • From β\beta, insert each element βj\beta_j in the form (βj,0)(\beta_j,0) into γ\gamma;
  • In the final linear basis γ\gamma, among all nonzero elements (ck,dk)(c_k,d_k), the ckc_k components of those with nonzero ckc_k form a linear basis of the union of α\alpha and β\beta, while, for those with zero ckc_k, the components dkd_k form a linear basis of the intersection of α\alpha and β\beta.

The method used to construct the linear basis can be the greedy method or Gaussian elimination, as long as the linear basis in γ\gamma forms a row-echelon matrix.

Comparing the elimination steps in the Zassenhaus algorithm with those in the preceding naive algorithm, it is easy to see that the greedy Zassenhaus algorithm is equivalent to the naive algorithm that maintains the contributions of elements in α\alpha. If we instead insert all (αi,0)(\alpha_i,0) first and then all (βj,βj)(\beta_j,\beta_j), the greedy Zassenhaus algorithm is equivalent to the naive algorithm that maintains the contributions of elements in β\beta. By the equivalence of the elimination steps, the Zassenhaus algorithm is also correct.

In addition, an independent and more general algebraic proof can be given:

Correctness Proof

Let VV be a linear space with subspaces U=span⁡αU=\operatorname{span}\alpha and W=span⁡βW=\operatorname{span}\beta. The algorithm itself amounts to reducing the subspace

H=span⁡({(αi,αi):αi∈α}∪{(βj,0):βj∈β})H = \operatorname{span}(\{(\alpha_i,\alpha_i):\alpha_i\in\alpha\}\cup\{(\beta_j,0):\beta_j\in\beta\})

to row-echelon form and finding a basis γ\gamma. At the end of the algorithm, the elements of γ\gamma, namely (ck,dk)(c_k,d_k), must be divided into two classes according to whether ck≠0c_k\neq 0. Consider the projection map π:H→V\pi:H\rightarrow V with π(a,b)=a\pi(a,b)=a. Then π(H)=U+W\pi(H)=U+W, and it is easy to verify that

ker⁡π=H∩({0}×V)={0}×(U×W).\begin{aligned} \ker\pi &= H\cap(\{0\}\times V) = \{0\}\times(U\times W). \end{aligned}

By the relevant theorem on linear maps, dim⁡H=dim⁡π(H)+dim⁡ker⁡π=dim⁡(U+W)+dim⁡(U∩W)\dim H = \dim\pi(H)+\dim\ker\pi = \dim(U+W)+\dim(U\cap W).

The first few columns of a row-echelon matrix are still row-echelon matrices. Therefore, the number of rows with ck≠0c_k\neq 0 is exactly the row rank of α∪β\alpha\cup\beta, namely dim⁡(U+W)\dim(U+W); moreover, the set of their ckc_k components forms a basis of U+WU+W. There are exactly dim⁡(U∩W)\dim(U\cap W) remaining nonzero rows, all satisfying ck=0c_k=0. For their dkd_k components, since (0,dk)∈ker⁡π(0,d_k)\in\ker\pi, we have dk∈U∩Wd_k\in U\cap W; and because (0,dk)(0,d_k) are rows of a row-echelon matrix, the vectors dkd_k are linearly independent. Thus these dkd_k form a linearly independent set in the intersection space U∩WU\cap W of size dim⁡(U∩W)\dim(U\cap W), so they must also form a basis of that space.

The template-problem code is as follows:

Code (Library Checker Intersection of F2\mathbf F_2 vector spaces)
#include <iostream>
#include <vector>

class LinearBasis {
  int K;
  std::vector<long long> a;

 public:
  LinearBasis(int K) : K(K), a(K) {}

  // Insert vector x.
  void insert(long long x) {
    for (int k = K - 1; ~k && x; --k) {
      if ((x >> k) & 1) {
        if (!a[k]) {
          a[k] = x;
        }
        x ^= a[k];
      }
    }
  }

  // Output those not exceeding 2^k.
  void print(int k) const {
    int sz = 0;
    for (int i = 0; i < k; ++i) {
      if (a[i]) {
        ++sz;
      }
    }
    std::cout << sz;
    for (int i = 0; i < k; ++i) {
      if (a[i]) {
        std::cout << ' ' << a[i];
      }
    }
    std::cout << '\n';
  }
};

int main() {
  constexpr int K = 30;
  int t;
  std::cin >> t;
  for (; t; --t) {
    LinearBasis c(K << 1);
    int n;
    std::cin >> n;
    for (; n; --n) {
      int x;
      std::cin >> x;
      c.insert(((long long)x << K) | x);
    }
    int m;
    std::cin >> m;
    for (; m; --m) {
      int x;
      std::cin >> x;
      c.insert((long long)x << K);
    }
    c.print(K);
  }
  return 0;
}

Note that when outputting, we only need to consider vectors whose first nn bits are all zero.

Extension: Prefix Linear Basis

This section discusses only XOR linear bases and assumes that a single vector can be stored in O(1)O(1) space and that each individual operation always has O(1)O(1) complexity.

For repeated queries of the maximum XOR on intervals, a common approach is to combine a cat tree with a linear basis, with time complexity O(nmlog⁡m+n2q)O(nm\log m+n^2q), where nn is the vector length, mm is the sequence length, and qq is the number of queries. Another feasible approach uses a prefix linear basis (also called a timestamp linear basis), reducing the complexity to O(n(m+q))O(n(m+q)).

A prefix linear basis maintains, for every prefix of a sequence, the linear bases of all suffixes of that prefix, thereby supporting queries for the linear basis of every interval. Observe that for a prefix [1,i][1,i], the linear bases of all its suffixes [j,i][j,i] are nested: the linear basis of [j,i][j,i] always contains that of [j+1,i][j+1,i]. Thus, there are at most nn distinct linear bases among these suffixes, and they can always be obtained by gradually adding new vectors to the empty set, from the suffix [i,i][i,i] through all suffixes up to [1,i][1,i]. By this monotonicity, it is enough to mark each added vector vv with the largest index tt at which it appears. Then all suffix linear bases can be stored in O(n)O(n) space. When querying the linear basis of interval [j,i][j,i], we only need to retain, in the prefix linear basis at ii, the vectors whose marks satisfy t≥jt\ge j.

For each vector vv, call its mark tt its timestamp. A vector vv in the linear basis can always be represented as the XOR sum of some elements of the original sequence, for example vi1⊕vi2⊕⋯⊕vikv_{i_1}\oplus v_{i_2}\oplus\cdots\oplus v_{i_k}. Among all such possible representations, the maximum of the minimum indices is tt, namely

t(v)=max⁡{j:∃i1,⋯ ,ik∈[j,i] s.t. v=vi1⊕vi2⊕⋯⊕vik}.t(v) = \max\{j:\exists i_1,\cdots,i_k\in[j,i]\text{ s.t. }v=v_{i_1}\oplus v_{i_2}\oplus\cdots\oplus v_{i_k}\}.

This expression merely writes the description in the preceding paragraph in formal language. It suggests that, to maintain the timestamp of each vector vv in the linear basis, we only need to greedily replace old vectors with the newest possible vectors.

When constructing a linear basis using the greedy method described above, the prefix linear basis makes the following adjustments during construction:

  • For every vector axa_x retained in the linear basis, store a timestamp txt_x, initially setting all of them to 00;
  • To add the ii-th vector vv in the sequence, still scan from the highest bit to the lowest, but also record the current time ii;
  • If vv has bit xx set, compare the timestamp of the existing vector axa_x in the linear basis, txt_x, with the current time ii:
    • If i>txi>t_x, that is, the vector being added is newer, set axa_x to vv, update its timestamp to ii, and continue the insertion using the old axa_x XORed with vv, namely ax⊕va_x\oplus v, with the old recorded time txt_x;
    • If i<txi<t_x, that is, the vector being added is older, do not update axa_x or txt_x; continue the insertion after XORing vv with axa_x.

In other words, if the current bit can be represented by a newer vector, use the newer vector directly; otherwise retain the original vector. When updating the vector at position xx, do not store the XOR result ax⊕va_x\oplus v at position xx, because the timestamp of ax⊕va_x\oplus v is min⁡{t(ax)=t(v)}=t(ax)\min\{t(a_x)=t(v)\}=t(a_x), which is smaller than the timestamp of the added vector vv, namely t(v)t(v). For the same reason, upward updates during construction by Gaussian elimination may break the timestamp property, so Gaussian elimination is no longer suitable for constructing a prefix linear basis.

The template-problem code is as follows:

Code (Codeforces 1100F Ivan and Burgers)
#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>
#include <vector>

class LinearBasis {
  static constexpr int K = 20;
  std::array<int, K> a, t;

 public:
  LinearBasis() : a{}, t{} {}

  // Insert vector x at time i.
  void insert(int x, int i) {
    for (int k = K - 1; ~k && x; --k) {
      if (((x >> k) & 1)) {
        if (i > t[k]) {
          std::swap(a[k], x);
          std::swap(t[k], i);
        }
        x ^= a[k];
      }
    }
  }

  // Find max xor of subsets of elements from time i till now.
  int query(int i) const {
    int res = 0;
    for (int k = K - 1; ~k; --k) {
      if (t[k] >= i && (res ^ a[k]) > res) {
        res ^= a[k];
      }
    }
    return res;
  }
};

int main() {
  int n;
  std::cin >> n;
  std::vector<int> c(n + 1);
  for (int i = 1; i <= n; ++i) std::cin >> c[i];
  int q;
  std::cin >> q;
  std::vector<std::array<int, 2>> qu(q);
  for (auto& v : qu) std::cin >> v[0] >> v[1];
  std::vector<int> ids(q);
  std::iota(ids.begin(), ids.end(), 0);
  std::sort(ids.begin(), ids.end(),
            [&](int l, int r) -> bool { return qu[l][1] < qu[r][1]; });
  LinearBasis lb;
  std::vector<int> res(q);
  for (int i = 1, j = 0; i <= n; ++i) {
    lb.insert(c[i], i);
    for (; j < q && qu[ids[j]][1] == i; ++j) {
      res[ids[j]] = lb.query(qu[ids[j]][0]);
    }
  }
  for (int x : res) std::cout << x << '\n';
  return 0;
}

If online queries are required, we can store the prefix linear basis at every prefix using O(mn)O(mn) space and then answer queries; this can be viewed as a persistent linear basis. If we need properties of the linear basis obtained by Gaussian elimination, they can be handled separately during queries.

Exercises

References and Notes

  1. Qiu Weisheng, Advanced Algebra (Volume II). Tsinghua University Press.
  2. Basis (linear algebra) - Wikipedia
  3. Vector Basis -- from Wolfram MathWorld
  4. Zassenhaus algorithm - Wikipedia

Footnotes

  1. Proof that every vector space has a basis ↩

This content comes from OI Wiki: Linear Basis. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.