Problem p02456 (C++) — buggy submission, verdict: Runtime Error

#include <algorithm>
#include <cassert>
#include <cstdio>
#include <functional>
#include <map>
#include <vector>

// for debug
#include <random>
#include <unistd.h>

// for release
#define fprintf(...) void(0)

template <class T> class RedBlackTree {
  struct Node {
    Node *children[2] = {nullptr, nullptr}, *parent = nullptr;
    T value;
    enum Color { RED, BLACK } color = RED;
    Node(T x) : value(x) {}
  } *root = nullptr;

  RedBlackTree(Node *root) : root(root) {
    if (root) {
      root->parent = nullptr;
      root->color = Node::BLACK;
    }
  }

  bool is_red(Node *node) const { return node && node->color == Node::RED; }

  size_t black_height() const {
    size_t res = 0;
    for (Node *cur = root; cur; cur = cur->children[0])
      if (cur->color == Node::BLACK)
        ++res;

    return res;
  }

  void rotate(Node *cur, int dir) {
    // dir == 0: left-rotate
    // dir == 1: right-rotate

    Node *child = cur->children[!dir];
    assert(child);
    cur->children[!dir] = child->children[dir];
    if (child->children[dir])
      child->children[dir]->parent = cur;

    child->parent = cur->parent;
    if (cur->parent == nullptr) {
      root = child;
    } else if (cur == cur->parent->children[dir]) {
      cur->parent->children[dir] = child;
    } else {
      cur->parent->children[!dir] = child;
    }
    child->children[dir] = cur;
    cur->parent = child;
  }

  Node *successor(Node *cur) const {
    if (cur->children[1]) {
      cur = cur->children[1];
      while (cur->children[0])
        cur = cur->children[0];

      return cur;
    }

    while (true) {
      if (cur->parent == nullptr)
        return nullptr;
      if (cur == cur->parent->children[0])
        return cur->parent;
      cur = cur->parent;
    }
  }

  void insert_fixup(Node *cur) {
    while (is_red(cur->parent)) {
      Node *gparent = cur->parent->parent;
      assert(gparent);

      int uncle_dir = (cur->parent != gparent->children[1]);
      Node *uncle = gparent->children[uncle_dir];

      if (is_red(uncle)) {
        cur->parent->color = uncle->color = Node::BLACK;
        gparent->color = Node::RED;
        cur = gparent;
        continue;
      }
      if (cur == cur->parent->children[uncle_dir]) {
        cur = cur->parent;
        rotate(cur, !uncle_dir);
      }
      cur->parent->color = Node::BLACK;
      cur->parent->parent->color = Node::RED;
      rotate(gparent, uncle_dir);
    }
    root->color = Node::BLACK;
  }

  void erase_fixup(Node *cur, Node *parent) {
    while (cur != root && !is_red(cur)) {
      int sibling_dir = (cur == parent->children[0]);
      Node *sibling = parent->children[sibling_dir];

      if (is_red(sibling)) {
        sibling->color = Node::BLACK;
        parent->color = Node::RED;
        rotate(parent, !sibling_dir);
        sibling = parent->children[sibling_dir];
      }

      if (sibling) {
        if (!is_red(sibling->children[0]) && !is_red(sibling->children[1])) {
          sibling->color = Node::RED;
          cur = parent;
          parent = cur->parent;
          continue;
        }

        if (!is_red(sibling->children[sibling_dir])) {
          if (sibling->children[!sibling_dir])
            sibling->children[!sibling_dir]->color = Node::BLACK;

          sibling->color = Node::RED;
          rotate(sibling, sibling_dir);
          sibling = parent->children[sibling_dir];
          assert(sibling);
        }

        sibling->color = parent->color;
        sibling->children[sibling_dir]->color = Node::BLACK;
      }

      parent->color = Node::BLACK;
      rotate(parent, !sibling_dir);
      cur = root;
      parent = nullptr;
    }
    if (cur)
      cur->color = Node::BLACK;
  }

public:
  RedBlackTree() = default;

  const Node *find(T x) const {
    const Node *cur = root;
    while (cur != nullptr) {
      if (cur->value == x)
        break;
      cur = cur->children[cur->value < x];
    }
    if (cur == nullptr)
      return nullptr;

    return cur;
  }

  bool contains(T x) const { return find(x) != nullptr; }

  std::pair<Node *, bool> insert(T x) {
    if (root == nullptr) {
      root = new Node(x);
      root->color = Node::BLACK;
      return {root, true};
    }

    Node **child = &root, *parent = nullptr;
    while (*child) {
      parent = *child;
      if ((*child)->value == x)
        return {*child, false};
      child = &(*child)->children[(*child)->value < x];
    }
    *child = new Node(x);
    (*child)->parent = parent;
    insert_fixup(*child);
    return {*child, true};
  }

  Node *erase(T x) {
    Node *cur = root;
    while (cur != nullptr) {
      if (cur->value == x)
        break;
      cur = cur->children[cur->value < x];
    }
    if (cur == nullptr)
      return nullptr;

    // Note: erased node should not be delete'd, but be just detached
    return erase(cur);
  }

  Node *erase(Node *cur) {
    if (cur == nullptr)
      return nullptr;

    Node *y = cur;
    if (cur->children[0] && cur->children[1])
      y = successor(y);
    assert(y != nullptr);

    Node *x = y->children[0];
    if (x == nullptr)
      x = y->children[1];

    if (x)
      x->parent = y->parent;
    if (y->parent == nullptr) {
      root = x;
    } else {
      y->parent->children[y == y->parent->children[1]] = x;
    }
    Node *xparent = y->parent; // not x->parent; in case x is null
    bool fix_needed = (y->color == Node::BLACK);
    if (y != cur) {
      // keep detached node intact; not use cur->value = y->value.
      y->parent = cur->parent;
      y->children[0] = cur->children[0];
      y->children[1] = cur->children[1];
      y->color = cur->color;

      if (y->children[0])
        y->children[0]->parent = y;
      if (y->children[1])
        y->children[1]->parent = y;
      if (y->parent) {
        y->parent->children[cur == cur->parent->children[1]] = y;
      } else {
        root = y;
      }

      if (xparent == cur) {
        xparent = y;
        if (x)
          x->parent = xparent;
      }
    }

    if (fix_needed)
      erase_fixup(x, xparent);

    cur->children[0] = cur->children[1] = cur->parent = nullptr;
    return cur;
  }

  bool verify() const {
    if (root == nullptr)
      return true;

    auto report = [](const char *s) {
      fprintf(stderr, "%sviolated:%s %s%s%s\\n", "\\x1b[31;1m", "\\x1b[0m",
              "\\x1b[37;1m", s, "\\x1b[0m");
    };

    bool violated = false;
    if (root->color == Node::RED) {
      violated = true;
      report("root node is red");
    }

    bool reached = false;
    size_t bheight = -1;
    std::function<void(Node *, int, size_t)> dfs = [&](Node *subroot, int depth,
                                                       size_t bh) {
      if (subroot->color == Node::RED) {
        // Note: once violated, we continue checking.  We may not assume
        //       the root node is black.
        if (subroot->parent && subroot->parent->color == Node::RED) {
          violated = true;
          report("two consecutive nodes are red");
        }
      } else {
        ++bh;
      }

      Node *left = subroot->children[0], *right = subroot->children[1];

      if (!(left && right)) {
        if (!reached) {
          bheight = bh;
          reached = true;
        } else if (bheight != bh) {
          violated = true;
          report("different number of black nodes in two root-leaf paths");
        }
      }

      if (right)
        dfs(right, depth + 1, bh);

      fprintf(stderr, "%s%*s%d%s (%zu)%s\\n",
              is_red(subroot) ? "\\x1b[31;1m" : "\\x1b[37;1m", depth * 2 + 2,
              "  ", subroot->value, "\\x1b[0m", bh, (left && right) ? "" : " *");

      if (left)
        dfs(left, depth + 1, bh);
    };

    fprintf(stderr, "================================================\\n");
    dfs(root, 0, 0);
    fprintf(stderr, "================================================\\n");
    return !violated;
  }
};

int main() {
  RedBlackTree<int> rbt;
  size_t size = 0;

  int q;
  scanf("%d", &q);

  for (int i = 0; i < q; ++i) {
    int t, x;
    scanf("%d %d", &t, &x);
    if (t == 0) {
      if (rbt.insert(x).second)
        ++size;
      printf("%zu\\n", size);
    } else if (t == 1) {
      printf("%d\\n", rbt.contains(x));
    } else if (t == 2) {
      if (rbt.contains(x))
        --size;
      rbt.erase(x);
    }
  }
  assert(rbt.verify());
}


Error: 0

Fixed submission:

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <vector>

#define fprintf(...) void(0)

template <class T> class RedBlackTree {
  struct Node {
    Node *left = nullptr, *right = nullptr, *parent = nullptr;
    T value;
    enum Color { Red = false, Black = true } color = Red;
    Node(T x) : value(x) {}
  } *root = nullptr;
  size_t size_ = 0;

  void left_rotate(Node *cur) {
    Node *right = cur->right;
    assert(right != nullptr);
    cur->right = right->left;
    if (right->left != nullptr)
      right->left->parent = cur;
    right->parent = cur->parent;
    if (cur->parent == nullptr) {
      root = right;
    } else if (cur == cur->parent->left) {
      cur->parent->left = right;
    } else {
      cur->parent->right = right;
    }
    right->left = cur;
    cur->parent = right;
  }

  void right_rotate(Node *cur) {
    Node *left = cur->left;
    assert(left != nullptr);
    cur->left = left->right;
    if (left->right != nullptr)
      left->right->parent = cur;
    left->parent = cur->parent;
    if (cur->parent == nullptr) {
      root = left;
    } else if (cur == cur->parent->right) {
      cur->parent->right = left;
    } else {
      cur->parent->left = left;
    }
    left->right = cur;
    cur->parent = left;
  }

  bool is_red(Node *node) const {
    // NIL is black
    return node != nullptr && node->color == Node::Red;
  }

  Node *successor(Node *cur) const {
    Node *prev = nullptr;
    if (cur->right == nullptr) {
      while (cur->parent != nullptr) {
        prev = cur;
        cur = cur->parent;
        if (cur->left == prev)
          break;
      }
    }
    cur = cur->right;
    prev = nullptr;
    while (cur != nullptr) {
      prev = cur;
      cur = cur->left;
    }
    return prev;
  }

  void insert_fixup(Node *cur) {
    while (is_red(cur->parent)) {
      Node *gparent = cur->parent->parent;
      assert(gparent != nullptr);

      Node *uncle = gparent->right;
      bool mirrored = false;
      if (cur->parent == uncle) {
        uncle = gparent->left;
        mirrored = true;
      }
      if (is_red(uncle)) {
        cur->parent->color = uncle->color = Node::Black;
        gparent->color = Node::Red;
        cur = gparent;
      } else {
        if ((cur == cur->parent->right) ^ mirrored) {
          cur = cur->parent;
          mirrored ? right_rotate(cur) : left_rotate(cur);
        }
        cur->parent->color = Node::Black;
        cur->parent->parent->color = Node::Red;
        mirrored ? left_rotate(gparent) : right_rotate(gparent);
      }
    }
    root->color = Node::Black;
  }

  void erase_fixup(Node *cur, Node *parent) {
    while (cur != root && !is_red(cur)) {
      Node *sibling = parent->right;
      bool mirrored = false;
      if (cur == sibling) {
        sibling = parent->left;
        mirrored = true;
      }

      if (is_red(sibling)) {
        sibling->color = Node::Black;
        parent->color = Node::Red;
        mirrored ? right_rotate(parent) : left_rotate(parent);
        sibling = (mirrored ? parent->left : parent->right);
      }

      if (sibling && !is_red(sibling->left) && !is_red(sibling->right)) {
        sibling->color = Node::Red;
        cur = parent;
        parent = cur->parent;
      } else {
        if (!mirrored) {
          if (sibling && !is_red(sibling->right)) {
            if (sibling->left != nullptr)
              sibling->left->color = Node::Black;

            sibling->color = Node::Red;
            right_rotate(sibling);
            sibling = parent->right;
          }
        } else {
          if (sibling && !is_red(sibling->left)) {
            if (sibling->right != nullptr)
              sibling->right->color = Node::Black;

            sibling->color = Node::Red;
            left_rotate(sibling);
            sibling = parent->left;
          }
        }
        if (sibling)
          sibling->color = parent->color;
        parent->color = Node::Black;
        if (sibling)
          (mirrored ? sibling->left : sibling->right)->color = Node::Black;

        mirrored ? right_rotate(parent) : left_rotate(parent);
        cur = root;
        parent = nullptr;
      }
    }
    assert(cur != nullptr);
    cur->color = Node::Black;
  }

public:
  RedBlackTree() = default;

  size_t size() const { return size_; }

  bool insert(T x) {
    if (root == nullptr) {
      root = new Node(x);
      root->color = Node::Black;
      ++size_;
      return true;
    }

    Node **child = &root, *parent = nullptr;
    while (*child != nullptr) {
      parent = *child;
      if ((*child)->value > x) {
        child = &(*child)->left;
      } else if ((*child)->value < x) {
        child = &(*child)->right;
      } else {
        return false;
      }
    }
    *child = new Node(x);
    ++size_;
    (*child)->parent = parent;
    insert_fixup(*child);
    return true;
  }

  bool erase(T x) {
    Node *cur = root;
    while (cur != nullptr) {
      if (cur->value > x) {
        cur = cur->left;
      } else if (cur->value < x) {
        cur = cur->right;
      } else {
        break;
      }
    }
    if (cur == nullptr)
      return false;

    --size_;
    return erase(cur);
  }

  bool erase(Node *cur) {
    Node *y = cur;
    if (cur->left != nullptr && cur->right != nullptr)
      y = successor(cur);

    assert(y != nullptr);
    Node *x = y->left;
    if (x == nullptr)
      x = y->right;

    if (x != nullptr)
      x->parent = y->parent;
    if (y->parent == nullptr) {
      root = x;
    } else if (y == y->parent->left) {
      y->parent->left = x;
    } else {
      y->parent->right = x;
    }

    if (y != cur) {
      cur->value = y->value;
    }
    if (y->color == Node::Black) {
      erase_fixup(x, y->parent);
    }
    return true;
  }

  bool contains(T x) const {
    Node *cur = root;
    while (cur != nullptr) {
      if (cur->value < x) {
        cur = cur->right;
      } else if (cur->value > x) {
        cur = cur->left;
      } else {
        return true;
      }
    }
    return false;
  }

  bool verify() const {
    if (root == nullptr)
      return true;

    bool violated = false;
    if (root->color == Node::Red) {
      violated = true;
      fprintf(stderr, "root node is red\\n");
    }

    bool reached = false;
    size_t bheight = -1;
    std::function<void(Node *, int, size_t)> dfs = [&](Node *subroot, int depth,
                                                       size_t bh) {
      if (subroot->color == Node::Red) {
        if (subroot->parent && subroot->parent->color == Node::Red) {
          violated = true;
          fprintf(stderr, "two red nodes are connected\\n");
        }
      } else {
        ++bh;
      }

      if (subroot->left == nullptr || subroot->right == nullptr) {
        if (!reached) {
          bheight = bh;
          reached = true;
        } else if (bheight != bh) {
          violated = true;
          fprintf(stderr,
                  "different number of black nodes in two root-leaf paths\\n");
        }
      }

      if (subroot->right != nullptr)
        dfs(subroot->right, depth + 1, bh);

      fprintf(stderr, "%s%*s%d%s (%zu) %c\\n",
              (subroot->color == Node::Red) ? "\\x1b[31;1m" : "\\x1b[37;1m",
              depth * 2 + 2, "  ", subroot->value, "\\x1b[0m", bh,
              (subroot->left && subroot->right) ? ' ' : '*');

      if (subroot->left != nullptr)
        dfs(subroot->left, depth + 1, bh);
    };
    fprintf(stderr, "================================================\\n");
    dfs(root, 0, 0);
    fprintf(stderr, "================================================\\n");
    return !violated;
  }
};

int main() {
  RedBlackTree<int> rbt;

  int q;
  scanf("%d", &q);
  for (int i = 0; i < q; ++i) {
    int t, x;
    scanf("%d", &t);
    if (t == 0) {
      scanf("%d", &x);
      rbt.insert(x);
      printf("%zu\\n", rbt.size());
    } else if (t == 1) {
      scanf("%d", &x);
      printf("%d\\n", rbt.contains(x));
    } else if (t == 2) {
      scanf("%d", &x);
      rbt.erase(x);
    }
  }
  assert(rbt.verify());
}