Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Elementary Algorithms / Arrays and Dynamic Programming

1.2.8 Minimum Excluded Value

1-Elementary-Algorithms/1.2.8_Minimum_Excluded_Value.cpp

View on GitHub

Computes the minimum excluded nonnegative integer, commonly called the MEX. The MEX of a set is the smallest integer $x \geq 0$ that does not appear in the set. It appears frequently in array problems, game theory with Grundy numbers, and dynamic programming states. Since the MEX of $n$ elements never exceeds $n$, the one-shot version simply marks which of the values $0$ to $n$ occur and scans for the first gap. The dynamic version keeps a count per value plus an ordered set of absent values, whose smallest member is always the current MEX.

  • mex(lo, hi) returns the MEX of the values in [lo, hi).
  • DynamicMex(n) maintains counts of values in [0, n], enough to track the MEX of a multiset of at most n relevant nonnegative values.
  • add(x) inserts one copy of value x if x $\in$ [0, n].
  • remove(x) removes one copy of value x if present.
  • get() returns the current MEX.

Implementation

#include <set>
#include <vector>

template<class It>
int mex(It lo, It hi) {
  int n = static_cast<int>(hi - lo);
  std::vector<bool> seen(n + 1, false);
  for (It it = lo; it != hi; ++it) {
    if (0 <= *it && *it <= n) {
      seen[*it] = true;
    }
  }
  for (int x = 0; x <= n; x++) {
    if (!seen[x]) {
      return x;
    }
  }
  return n + 1;
}

class DynamicMex {
  std::vector<int> count;
  std::set<int> missing;

 public:
  explicit DynamicMex(int n) : count(n + 1, 0) {
    for (int x = 0; x <= n; x++) {
      missing.insert(x);
    }
  }

  void add(int x) {
    if (0 <= x && x < static_cast<int>(count.size()) && count[x]++ == 0) {
      missing.erase(x);
    }
  }

  void remove(int x) {
    if (0 <= x && x < static_cast<int>(count.size()) && count[x] > 0 && --count[x] == 0) {
      missing.insert(x);
    }
  }

  int get() const { return *missing.begin(); }
};

Example Usage

#include <cassert>
using namespace std;

int main() {
  vector<int> a{0, 1, 4, 2, 1};
  assert(mex(a.begin(), a.end()) == 3);

  DynamicMex m(5);
  m.add(0);
  m.add(1);
  m.add(3);
  assert(m.get() == 2);
  m.add(2);
  assert(m.get() == 4);
  m.remove(1);
  assert(m.get() == 1);
  return 0;
}