Skip to content

mergeable_frequency_tree.hpp

SECTIONData Structure INCLUDEnoya/mergeable_frequency_tree.hpp

Dynamic frequency segment-tree pool supporting point updates, rank queries, destructive merging, and interval extraction in O(log U).

维护值域频次线段树并支持破坏性合并;适合树上启发式合并、子树频率和顺序统计。

Implementation

View on GitHub

#ifndef NOYA_MERGEABLE_FREQUENCY_TREE_HPP
#define NOYA_MERGEABLE_FREQUENCY_TREE_HPP 1

/// @complexity Time: O(log U) update/query; O(min(nodes_a,nodes_b)) destructive merge.
/// Space: O(number of allocated nodes).

#include <cassert>
#include <numeric>
#include <vector>

namespace noya {

/// @brief Dynamic frequency segment-tree pool supporting point updates, rank
/// queries, destructive merging, and interval extraction in O(log U).
template <class Count = int> struct mergeable_frequency_tree {
  struct node {
    int left = 0;
    int right = 0;
    Count sum{};
  };

  int lower = 0;
  int upper = 0;
  std::vector<node> nodes{{}};

  mergeable_frequency_tree() = default;
  mergeable_frequency_tree(int lower_bound, int upper_bound)
      : lower(lower_bound), upper(upper_bound) {
    assert(lower < upper);
  }

  Count total(int root) const {
    check_root(root);
    return nodes[root].sum;
  }

  /// @brief Add delta at position, creating root and path nodes as needed.
  void add(int &root, int position, Count delta) {
    assert(lower <= position && position < upper);
    add_impl(root, lower, upper, position, delta);
  }

  /// @brief Return the total frequency in [query_left, query_right).
  Count count(int root, int query_left, int query_right) const {
    check_root(root);
    assert(lower <= query_left && query_left <= query_right &&
           query_right <= upper);
    return count_impl(root, lower, upper, query_left, query_right);
  }

  /// @brief Return the coordinate containing zero-indexed rank k.
  int kth(int root, Count k) const {
    check_root(root);
    assert(Count{} <= k && k < nodes[root].sum);
    int left_bound = lower;
    int right_bound = upper;
    while (right_bound - left_bound > 1) {
      Count left_sum = nodes[nodes[root].left].sum;
      int middle = std::midpoint(left_bound, right_bound);
      if (k < left_sum) {
        root = nodes[root].left;
        right_bound = middle;
      } else {
        k -= left_sum;
        root = nodes[root].right;
        left_bound = middle;
      }
    }
    return left_bound;
  }

  /// @brief Move every frequency in [query_left, query_right) from root into
  /// a new root without duplicating leaves.
  int extract(int &root, int query_left, int query_right) {
    check_root(root);
    assert(lower <= query_left && query_left <= query_right &&
           query_right <= upper);
    return extract_impl(root, lower, upper, query_left, query_right);
  }

  /// @brief Destructively merge source into destination and clear source.
  void merge_into(int &destination, int &source) {
    check_root(destination);
    check_root(source);
    if (destination == source && destination != 0) {
      assert(false && "cannot merge a root into itself");
    }
    destination = merge_impl(destination, source);
    source = 0;
  }

private:
  void check_root(int root) const {
    assert(0 <= root && root < int(nodes.size()));
  }

  int make_node() {
    nodes.push_back({});
    return int(nodes.size()) - 1;
  }

  void pull(int root) {
    nodes[root].sum =
        nodes[nodes[root].left].sum + nodes[nodes[root].right].sum;
  }

  void add_impl(int &root, int left_bound, int right_bound, int position,
                Count delta) {
    if (root == 0) {
      root = make_node();
    }
    if (right_bound - left_bound == 1) {
      nodes[root].sum += delta;
      assert(nodes[root].sum >= Count{});
      return;
    }
    int middle = std::midpoint(left_bound, right_bound);
    if (position < middle) {
      int child = nodes[root].left;
      add_impl(child, left_bound, middle, position, delta);
      nodes[root].left = child;
    } else {
      int child = nodes[root].right;
      add_impl(child, middle, right_bound, position, delta);
      nodes[root].right = child;
    }
    pull(root);
  }

  Count count_impl(int root, int left_bound, int right_bound, int query_left,
                   int query_right) const {
    if (root == 0 || query_right <= left_bound || right_bound <= query_left) {
      return Count{};
    }
    if (query_left <= left_bound && right_bound <= query_right) {
      return nodes[root].sum;
    }
    int middle = std::midpoint(left_bound, right_bound);
    return count_impl(nodes[root].left, left_bound, middle, query_left,
                      query_right) +
           count_impl(nodes[root].right, middle, right_bound, query_left,
                      query_right);
  }

  int extract_impl(int &root, int left_bound, int right_bound, int query_left,
                   int query_right) {
    if (root == 0 || query_right <= left_bound || right_bound <= query_left) {
      return 0;
    }
    if (query_left <= left_bound && right_bound <= query_right) {
      int result = root;
      root = 0;
      return result;
    }
    int result = make_node();
    int middle = std::midpoint(left_bound, right_bound);
    int left_child = nodes[root].left;
    int right_child = nodes[root].right;
    nodes[result].left = extract_impl(left_child, left_bound, middle,
                                      query_left, query_right);
    nodes[result].right = extract_impl(right_child, middle, right_bound,
                                       query_left, query_right);
    nodes[root].left = left_child;
    nodes[root].right = right_child;
    pull(root);
    pull(result);
    if (nodes[result].sum == Count{}) {
      return 0;
    }
    return result;
  }

  int merge_impl(int destination, int source) {
    if (destination == 0 || source == 0) {
      return destination | source;
    }
    int destination_left = nodes[destination].left;
    int destination_right = nodes[destination].right;
    destination_left = merge_impl(destination_left, nodes[source].left);
    destination_right = merge_impl(destination_right, nodes[source].right);
    nodes[destination].left = destination_left;
    nodes[destination].right = destination_right;
    if (destination_left == 0 && destination_right == 0) {
      nodes[destination].sum += nodes[source].sum;
    } else {
      pull(destination);
    }
    return destination;
  }
};

} // namespace noya

#endif // NOYA_MERGEABLE_FREQUENCY_TREE_HPP
#include <cassert>
#include <numeric>
#include <vector>

/// @complexity Time: O(log U) update/query; O(min(nodes_a,nodes_b)) destructive merge.
/// Space: O(number of allocated nodes).

namespace noya {

/// @brief Dynamic frequency segment-tree pool supporting point updates, rank
/// queries, destructive merging, and interval extraction in O(log U).
template <class Count = int> struct mergeable_frequency_tree {
  struct node {
    int left = 0;
    int right = 0;
    Count sum{};
  };

  int lower = 0;
  int upper = 0;
  std::vector<node> nodes{{}};

  mergeable_frequency_tree() = default;
  mergeable_frequency_tree(int lower_bound, int upper_bound)
      : lower(lower_bound), upper(upper_bound) {
    assert(lower < upper);
  }

  Count total(int root) const {
    check_root(root);
    return nodes[root].sum;
  }

  /// @brief Add delta at position, creating root and path nodes as needed.
  void add(int &root, int position, Count delta) {
    assert(lower <= position && position < upper);
    add_impl(root, lower, upper, position, delta);
  }

  /// @brief Return the total frequency in [query_left, query_right).
  Count count(int root, int query_left, int query_right) const {
    check_root(root);
    assert(lower <= query_left && query_left <= query_right &&
           query_right <= upper);
    return count_impl(root, lower, upper, query_left, query_right);
  }

  /// @brief Return the coordinate containing zero-indexed rank k.
  int kth(int root, Count k) const {
    check_root(root);
    assert(Count{} <= k && k < nodes[root].sum);
    int left_bound = lower;
    int right_bound = upper;
    while (right_bound - left_bound > 1) {
      Count left_sum = nodes[nodes[root].left].sum;
      int middle = std::midpoint(left_bound, right_bound);
      if (k < left_sum) {
        root = nodes[root].left;
        right_bound = middle;
      } else {
        k -= left_sum;
        root = nodes[root].right;
        left_bound = middle;
      }
    }
    return left_bound;
  }

  /// @brief Move every frequency in [query_left, query_right) from root into
  /// a new root without duplicating leaves.
  int extract(int &root, int query_left, int query_right) {
    check_root(root);
    assert(lower <= query_left && query_left <= query_right &&
           query_right <= upper);
    return extract_impl(root, lower, upper, query_left, query_right);
  }

  /// @brief Destructively merge source into destination and clear source.
  void merge_into(int &destination, int &source) {
    check_root(destination);
    check_root(source);
    if (destination == source && destination != 0) {
      assert(false && "cannot merge a root into itself");
    }
    destination = merge_impl(destination, source);
    source = 0;
  }

private:
  void check_root(int root) const {
    assert(0 <= root && root < int(nodes.size()));
  }

  int make_node() {
    nodes.push_back({});
    return int(nodes.size()) - 1;
  }

  void pull(int root) {
    nodes[root].sum =
        nodes[nodes[root].left].sum + nodes[nodes[root].right].sum;
  }

  void add_impl(int &root, int left_bound, int right_bound, int position,
                Count delta) {
    if (root == 0) {
      root = make_node();
    }
    if (right_bound - left_bound == 1) {
      nodes[root].sum += delta;
      assert(nodes[root].sum >= Count{});
      return;
    }
    int middle = std::midpoint(left_bound, right_bound);
    if (position < middle) {
      int child = nodes[root].left;
      add_impl(child, left_bound, middle, position, delta);
      nodes[root].left = child;
    } else {
      int child = nodes[root].right;
      add_impl(child, middle, right_bound, position, delta);
      nodes[root].right = child;
    }
    pull(root);
  }

  Count count_impl(int root, int left_bound, int right_bound, int query_left,
                   int query_right) const {
    if (root == 0 || query_right <= left_bound || right_bound <= query_left) {
      return Count{};
    }
    if (query_left <= left_bound && right_bound <= query_right) {
      return nodes[root].sum;
    }
    int middle = std::midpoint(left_bound, right_bound);
    return count_impl(nodes[root].left, left_bound, middle, query_left,
                      query_right) +
           count_impl(nodes[root].right, middle, right_bound, query_left,
                      query_right);
  }

  int extract_impl(int &root, int left_bound, int right_bound, int query_left,
                   int query_right) {
    if (root == 0 || query_right <= left_bound || right_bound <= query_left) {
      return 0;
    }
    if (query_left <= left_bound && right_bound <= query_right) {
      int result = root;
      root = 0;
      return result;
    }
    int result = make_node();
    int middle = std::midpoint(left_bound, right_bound);
    int left_child = nodes[root].left;
    int right_child = nodes[root].right;
    nodes[result].left = extract_impl(left_child, left_bound, middle,
                                      query_left, query_right);
    nodes[result].right = extract_impl(right_child, middle, right_bound,
                                       query_left, query_right);
    nodes[root].left = left_child;
    nodes[root].right = right_child;
    pull(root);
    pull(result);
    if (nodes[result].sum == Count{}) {
      return 0;
    }
    return result;
  }

  int merge_impl(int destination, int source) {
    if (destination == 0 || source == 0) {
      return destination | source;
    }
    int destination_left = nodes[destination].left;
    int destination_right = nodes[destination].right;
    destination_left = merge_impl(destination_left, nodes[source].left);
    destination_right = merge_impl(destination_right, nodes[source].right);
    nodes[destination].left = destination_left;
    nodes[destination].right = destination_right;
    if (destination_left == 0 && destination_right == 0) {
      nodes[destination].sum += nodes[source].sum;
    } else {
      pull(destination);
    }
    return destination;
  }
};

} // namespace noya