Skip to content

binary_trie.hpp

SECTIONData Structure INCLUDEnoya/binary_trie.hpp

Multiset of fixed-width unsigned integers with xor-order queries.

Verified by ordered_set, set_xor_min.

维护整数的二进制字典树,支持插入删除、异或最值和按异或序第 k 小;适合动态 XOR 查询。

Implementation

View on GitHub

#ifndef NOYA_BINARY_TRIE_HPP
#define NOYA_BINARY_TRIE_HPP 1

/// @complexity Time: O(B) per update or query for B-bit keys.
/// Space: O(NB) nodes after N distinct insertion paths.

#include <array>
#include <cassert>
#include <cstdint>
#include <limits>
#include <vector>

namespace noya {

/// @brief Multiset of fixed-width unsigned integers with xor-order queries.
template <int Bits, class UInt = std::uint64_t> struct binary_trie {
  static_assert(Bits > 0);
  static_assert(Bits <= std::numeric_limits<UInt>::digits);

  struct node {
    std::array<int, 2> child = {-1, -1};
    int count = 0;
  };

  std::vector<node> nodes = {node{}};

  int size() const { return nodes[0].count; }
  bool empty() const { return size() == 0; }

  /// @brief Insert one occurrence of value in O(Bits).
  void insert(UInt value) {
    assert(in_range(value));
    int current = 0;
    nodes[current].count++;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int direction = int((value >> bit) & UInt(1));
      if (nodes[current].child[direction] == -1) {
        nodes[current].child[direction] = int(nodes.size());
        nodes.push_back(node{});
      }
      current = nodes[current].child[direction];
      nodes[current].count++;
    }
  }

  /// @brief Erase one occurrence of value, returning whether it existed.
  bool erase(UInt value) {
    assert(in_range(value));
    if (count(value) == 0) {
      return false;
    }
    int current = 0;
    nodes[current].count--;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int direction = int((value >> bit) & UInt(1));
      current = nodes[current].child[direction];
      nodes[current].count--;
    }
    return true;
  }

  int count(UInt value) const {
    assert(in_range(value));
    int current = 0;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int direction = int((value >> bit) & UInt(1));
      current = nodes[current].child[direction];
      if (current == -1 || nodes[current].count == 0) {
        return 0;
      }
    }
    return nodes[current].count;
  }

  /// @brief Return the k-th smallest value after xor by xor_mask.
  UInt kth_xor(int k, UInt xor_mask = 0) const {
    assert(0 <= k && k < size());
    assert(in_range(xor_mask));
    int current = 0;
    UInt result = 0;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int mask_bit = int((xor_mask >> bit) & UInt(1));
      int preferred = nodes[current].child[mask_bit];
      int preferred_count = preferred == -1 ? 0 : nodes[preferred].count;
      if (k < preferred_count) {
        current = preferred;
      } else {
        k -= preferred_count;
        current = nodes[current].child[mask_bit ^ 1];
        result |= UInt(1) << bit;
      }
      assert(current != -1);
    }
    return result;
  }

  UInt min_xor(UInt xor_mask = 0) const { return kth_xor(0, xor_mask); }
  UInt max_xor(UInt xor_mask = 0) const {
    return kth_xor(size() - 1, xor_mask);
  }

  /// @brief Count values x satisfying (x xor xor_mask) < upper.
  int count_less(UInt upper, UInt xor_mask = 0) const {
    assert(in_range(xor_mask));
    if constexpr (Bits < std::numeric_limits<UInt>::digits) {
      if ((upper >> Bits) != 0) {
        return size();
      }
    }
    int current = 0;
    int result = 0;
    for (int bit = Bits - 1; bit >= 0 && current != -1; bit--) {
      int mask_bit = int((xor_mask >> bit) & UInt(1));
      int upper_bit = int((upper >> bit) & UInt(1));
      if (upper_bit != 0) {
        int smaller = nodes[current].child[mask_bit];
        if (smaller != -1) {
          result += nodes[smaller].count;
        }
        current = nodes[current].child[mask_bit ^ 1];
      } else {
        current = nodes[current].child[mask_bit];
      }
    }
    return result;
  }

private:
  static bool in_range(UInt value) {
    if constexpr (Bits == std::numeric_limits<UInt>::digits) {
      return true;
    } else {
      return (value >> Bits) == 0;
    }
  }
};

} // namespace noya

#endif // NOYA_BINARY_TRIE_HPP
#include <array>
#include <cassert>
#include <cstdint>
#include <limits>
#include <vector>

/// @complexity Time: O(B) per update or query for B-bit keys.
/// Space: O(NB) nodes after N distinct insertion paths.

namespace noya {

/// @brief Multiset of fixed-width unsigned integers with xor-order queries.
template <int Bits, class UInt = std::uint64_t> struct binary_trie {
  static_assert(Bits > 0);
  static_assert(Bits <= std::numeric_limits<UInt>::digits);

  struct node {
    std::array<int, 2> child = {-1, -1};
    int count = 0;
  };

  std::vector<node> nodes = {node{}};

  int size() const { return nodes[0].count; }
  bool empty() const { return size() == 0; }

  /// @brief Insert one occurrence of value in O(Bits).
  void insert(UInt value) {
    assert(in_range(value));
    int current = 0;
    nodes[current].count++;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int direction = int((value >> bit) & UInt(1));
      if (nodes[current].child[direction] == -1) {
        nodes[current].child[direction] = int(nodes.size());
        nodes.push_back(node{});
      }
      current = nodes[current].child[direction];
      nodes[current].count++;
    }
  }

  /// @brief Erase one occurrence of value, returning whether it existed.
  bool erase(UInt value) {
    assert(in_range(value));
    if (count(value) == 0) {
      return false;
    }
    int current = 0;
    nodes[current].count--;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int direction = int((value >> bit) & UInt(1));
      current = nodes[current].child[direction];
      nodes[current].count--;
    }
    return true;
  }

  int count(UInt value) const {
    assert(in_range(value));
    int current = 0;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int direction = int((value >> bit) & UInt(1));
      current = nodes[current].child[direction];
      if (current == -1 || nodes[current].count == 0) {
        return 0;
      }
    }
    return nodes[current].count;
  }

  /// @brief Return the k-th smallest value after xor by xor_mask.
  UInt kth_xor(int k, UInt xor_mask = 0) const {
    assert(0 <= k && k < size());
    assert(in_range(xor_mask));
    int current = 0;
    UInt result = 0;
    for (int bit = Bits - 1; bit >= 0; bit--) {
      int mask_bit = int((xor_mask >> bit) & UInt(1));
      int preferred = nodes[current].child[mask_bit];
      int preferred_count = preferred == -1 ? 0 : nodes[preferred].count;
      if (k < preferred_count) {
        current = preferred;
      } else {
        k -= preferred_count;
        current = nodes[current].child[mask_bit ^ 1];
        result |= UInt(1) << bit;
      }
      assert(current != -1);
    }
    return result;
  }

  UInt min_xor(UInt xor_mask = 0) const { return kth_xor(0, xor_mask); }
  UInt max_xor(UInt xor_mask = 0) const {
    return kth_xor(size() - 1, xor_mask);
  }

  /// @brief Count values x satisfying (x xor xor_mask) < upper.
  int count_less(UInt upper, UInt xor_mask = 0) const {
    assert(in_range(xor_mask));
    if constexpr (Bits < std::numeric_limits<UInt>::digits) {
      if ((upper >> Bits) != 0) {
        return size();
      }
    }
    int current = 0;
    int result = 0;
    for (int bit = Bits - 1; bit >= 0 && current != -1; bit--) {
      int mask_bit = int((xor_mask >> bit) & UInt(1));
      int upper_bit = int((upper >> bit) & UInt(1));
      if (upper_bit != 0) {
        int smaller = nodes[current].child[mask_bit];
        if (smaller != -1) {
          result += nodes[smaller].count;
        }
        current = nodes[current].child[mask_bit ^ 1];
      } else {
        current = nodes[current].child[mask_bit];
      }
    }
    return result;
  }

private:
  static bool in_range(UInt value) {
    if constexpr (Bits == std::numeric_limits<UInt>::digits) {
      return true;
    } else {
      return (value >> Bits) == 0;
    }
  }
};

} // namespace noya