Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Elementary Algorithms / Miscellaneous Problems

Given a multiset of integers, find the minimum excluded nonnegative value (MEX), i.e. the smallest integer $x \geq 0$ not present in the multiset. MEX often appears in array problems, game theory with Grundy numbers, and dynamic programming states. Since the MEX of $n$ values never exceeds $n$, the one-shot version tracks seen values in $[0, n]$ before scanning to find the first missing.

  • mex(lo, hi) returns the MEX of the values in $[{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{hi}}})$.

The MEX of a dynamic multiset can be computed using counts of present values and maintaining an ordered set of currently missing candidates.

  • DynamicMex() maintains the MEX of a multiset of nonnegative integers.
  • add(x) inserts one copy of value x if x $\geq 0$.
  • remove(x) removes one copy of value x if present.
  • mex() returns the current MEX.

Implementation

#include <iterator>
#include <set>
#include <unordered_map>
#include <vector>

template<typename It>
int mex(It lo, It hi) {
  int n = std::distance(lo, hi);
  std::vector<char> seen(n + 1);
  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::unordered_map<int, int> count;
  std::set<int> missing;  // Missing candidates in [0, size].
  int size = 0;

 public:
  DynamicMex() { missing.insert(0); }

  void add(int x) {
    if (x < 0) {
      return;
    }
    if (!count.count(size + 1)) {
      missing.insert(size + 1);
    }
    if (count[x]++ == 0) {
      missing.erase(x);
    }
    size++;
  }

  void remove(int x) {
    auto it = count.find(x);
    if (it == count.end()) {
      return;
    }
    bool removed_last = --it->second == 0;
    if (removed_last) {
      count.erase(it);
    }
    size--;
    missing.erase(size + 1);
    if (removed_last && x <= size) {
      missing.insert(x);
    }
  }

  int mex() 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);
  vector<int> b{-1, 0, 1, 2};
  assert(mex(b.begin(), b.end()) == 3);  // Negative values are ignored.

  DynamicMex m;
  m.add(0);
  m.add(1);
  m.add(1);
  m.add(3);
  assert(m.mex() == 2);
  m.add(2);
  assert(m.mex() == 4);
  m.remove(1);
  assert(m.mex() == 4);  // One copy of 1 remains.
  m.remove(1);
  assert(m.mex() == 1);
  m.remove(100);
  assert(m.mex() == 1);
  return 0;
}