Skip to content

link_cut_tree.hpp

SECTIONData Structure INCLUDEnoya/link_cut_tree.hpp

Link-Cut Tree for a dynamic forest with point assignment and ordered path products; all operations take amortized O(log n) time.

Verified by dynamic_tree_vertex_add_path_sum, dynamic_tree_vertex_set_path_composite.

维护动态森林的连边、断边、换根及路径信息;题目在线改变树结构并查询路径时使用。

Implementation

View on GitHub

#ifndef NOYA_LINK_CUT_TREE_HPP
#define NOYA_LINK_CUT_TREE_HPP 1

/// @complexity Time: Amortized O(log n) per dynamic-tree operation.
/// Space: O(n).

#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>

namespace noya {

/// @brief Link-Cut Tree for a dynamic forest with point assignment and ordered
/// path products; all operations take amortized O(log n) time.
template <class Monoid> struct link_cut_tree {
  using value_type = typename Monoid::value_type;

  struct node {
    int child[2] = {-1, -1};
    int parent = -1;
    int auxiliary_size = 1;
    bool reversed = false;
    value_type value;
    value_type forward_product;
    value_type backward_product;

    explicit node(const value_type &initial)
        : value(initial), forward_product(initial), backward_product(initial) {}
  };

  std::vector<node> nodes;

  link_cut_tree() = default;

  explicit link_cut_tree(const std::vector<value_type> &values) {
    build(values);
  }

  /// @brief Reset to isolated vertices carrying the given values.
  void build(const std::vector<value_type> &values) {
    nodes.clear();
    nodes.reserve(values.size());
    splay_stack.clear();
    splay_stack.reserve(values.size());
    for (const value_type &value : values) {
      nodes.emplace_back(value);
    }
  }

  /// @brief Return the number of vertices.
  int size() const { return int(nodes.size()); }

  /// @brief Return whether two vertices are in the same represented tree.
  bool connected(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    return first == second || find_root(first) == find_root(second);
  }

  /// @brief Add an edge between different trees; return false if it would
  /// create a cycle.
  bool link(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    if (connected(first, second)) {
      return false;
    }
    make_root(first);
    nodes[first].parent = second;
    return true;
  }

  /// @brief Remove an existing edge; return false when the vertices are not
  /// directly adjacent.
  bool cut(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    make_root(first);
    access(second);
    if (nodes[second].child[0] != first || nodes[first].child[1] != -1) {
      return false;
    }
    nodes[second].child[0] = -1;
    nodes[first].parent = -1;
    pull(second);
    return true;
  }

  /// @brief Change one vertex value.
  void set(int vertex, const value_type &value) {
    check_vertex(vertex);
    access(vertex);
    nodes[vertex].value = value;
    pull(vertex);
  }

  /// @brief Return one vertex value.
  const value_type &get(int vertex) {
    check_vertex(vertex);
    access(vertex);
    return nodes[vertex].value;
  }

  /// @brief Return the ordered monoid product on the path from first to
  /// second; the vertices must be connected.
  value_type path_product(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    assert(connected(first, second));
    make_root(first);
    access(second);
    return nodes[second].forward_product;
  }

  /// @brief Return the number of vertices on a connected path.
  int path_size(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    assert(connected(first, second));
    make_root(first);
    access(second);
    return nodes[second].auxiliary_size;
  }

private:
  std::vector<int> splay_stack;

  void check_vertex(int vertex) const {
    assert(0 <= vertex && vertex < size());
  }

  bool is_auxiliary_root(int vertex) const {
    int parent = nodes[vertex].parent;
    return parent == -1 || (nodes[parent].child[0] != vertex &&
                            nodes[parent].child[1] != vertex);
  }

  int auxiliary_size(int vertex) const {
    return vertex == -1 ? 0 : nodes[vertex].auxiliary_size;
  }

  value_type forward_product(int vertex) const {
    return vertex == -1 ? Monoid::unit() : nodes[vertex].forward_product;
  }

  value_type backward_product(int vertex) const {
    return vertex == -1 ? Monoid::unit() : nodes[vertex].backward_product;
  }

  void pull(int vertex) {
    int left = nodes[vertex].child[0];
    int right = nodes[vertex].child[1];
    nodes[vertex].auxiliary_size =
        1 + auxiliary_size(left) + auxiliary_size(right);
    nodes[vertex].forward_product =
        Monoid::op(Monoid::op(forward_product(left), nodes[vertex].value),
                   forward_product(right));
    nodes[vertex].backward_product =
        Monoid::op(Monoid::op(backward_product(right), nodes[vertex].value),
                   backward_product(left));
  }

  void apply_reverse(int vertex) {
    if (vertex == -1) {
      return;
    }
    std::swap(nodes[vertex].child[0], nodes[vertex].child[1]);
    std::swap(nodes[vertex].forward_product,
              nodes[vertex].backward_product);
    nodes[vertex].reversed = !nodes[vertex].reversed;
  }

  void push(int vertex) {
    if (!nodes[vertex].reversed) {
      return;
    }
    apply_reverse(nodes[vertex].child[0]);
    apply_reverse(nodes[vertex].child[1]);
    nodes[vertex].reversed = false;
  }

  void rotate(int vertex) {
    int parent = nodes[vertex].parent;
    int grandparent = nodes[parent].parent;
    int direction = nodes[parent].child[1] == vertex;
    int middle = nodes[vertex].child[direction ^ 1];

    if (!is_auxiliary_root(parent)) {
      nodes[grandparent].child[nodes[grandparent].child[1] == parent] = vertex;
    }
    nodes[vertex].parent = grandparent;
    nodes[vertex].child[direction ^ 1] = parent;
    nodes[parent].parent = vertex;
    nodes[parent].child[direction] = middle;
    if (middle != -1) {
      nodes[middle].parent = parent;
    }
    pull(parent);
    pull(vertex);
  }

  void splay(int vertex) {
    splay_stack.clear();
    splay_stack.push_back(vertex);
    for (int current = vertex; !is_auxiliary_root(current);) {
      current = nodes[current].parent;
      splay_stack.push_back(current);
    }
    for (auto iterator = splay_stack.rbegin(); iterator != splay_stack.rend();
         ++iterator) {
      push(*iterator);
    }

    while (!is_auxiliary_root(vertex)) {
      int parent = nodes[vertex].parent;
      int grandparent = nodes[parent].parent;
      if (!is_auxiliary_root(parent)) {
        bool vertex_right = nodes[parent].child[1] == vertex;
        bool parent_right = nodes[grandparent].child[1] == parent;
        rotate(vertex_right == parent_right ? parent : vertex);
      }
      rotate(vertex);
    }
  }

  int access(int vertex) {
    int last = -1;
    for (int current = vertex; current != -1;) {
      splay(current);
      int path_parent = nodes[current].parent;
      nodes[current].child[1] = last;
      if (last != -1) {
        nodes[last].parent = current;
      }
      pull(current);
      last = current;
      current = path_parent;
    }
    splay(vertex);
    return last;
  }

  void make_root(int vertex) {
    access(vertex);
    apply_reverse(vertex);
  }

  int find_root(int vertex) {
    access(vertex);
    push(vertex);
    while (nodes[vertex].child[0] != -1) {
      vertex = nodes[vertex].child[0];
      push(vertex);
    }
    splay(vertex);
    return vertex;
  }
};

} // namespace noya

#endif // NOYA_LINK_CUT_TREE_HPP
#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>

/// @complexity Time: Amortized O(log n) per dynamic-tree operation.
/// Space: O(n).

namespace noya {

/// @brief Link-Cut Tree for a dynamic forest with point assignment and ordered
/// path products; all operations take amortized O(log n) time.
template <class Monoid> struct link_cut_tree {
  using value_type = typename Monoid::value_type;

  struct node {
    int child[2] = {-1, -1};
    int parent = -1;
    int auxiliary_size = 1;
    bool reversed = false;
    value_type value;
    value_type forward_product;
    value_type backward_product;

    explicit node(const value_type &initial)
        : value(initial), forward_product(initial), backward_product(initial) {}
  };

  std::vector<node> nodes;

  link_cut_tree() = default;

  explicit link_cut_tree(const std::vector<value_type> &values) {
    build(values);
  }

  /// @brief Reset to isolated vertices carrying the given values.
  void build(const std::vector<value_type> &values) {
    nodes.clear();
    nodes.reserve(values.size());
    splay_stack.clear();
    splay_stack.reserve(values.size());
    for (const value_type &value : values) {
      nodes.emplace_back(value);
    }
  }

  /// @brief Return the number of vertices.
  int size() const { return int(nodes.size()); }

  /// @brief Return whether two vertices are in the same represented tree.
  bool connected(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    return first == second || find_root(first) == find_root(second);
  }

  /// @brief Add an edge between different trees; return false if it would
  /// create a cycle.
  bool link(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    if (connected(first, second)) {
      return false;
    }
    make_root(first);
    nodes[first].parent = second;
    return true;
  }

  /// @brief Remove an existing edge; return false when the vertices are not
  /// directly adjacent.
  bool cut(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    make_root(first);
    access(second);
    if (nodes[second].child[0] != first || nodes[first].child[1] != -1) {
      return false;
    }
    nodes[second].child[0] = -1;
    nodes[first].parent = -1;
    pull(second);
    return true;
  }

  /// @brief Change one vertex value.
  void set(int vertex, const value_type &value) {
    check_vertex(vertex);
    access(vertex);
    nodes[vertex].value = value;
    pull(vertex);
  }

  /// @brief Return one vertex value.
  const value_type &get(int vertex) {
    check_vertex(vertex);
    access(vertex);
    return nodes[vertex].value;
  }

  /// @brief Return the ordered monoid product on the path from first to
  /// second; the vertices must be connected.
  value_type path_product(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    assert(connected(first, second));
    make_root(first);
    access(second);
    return nodes[second].forward_product;
  }

  /// @brief Return the number of vertices on a connected path.
  int path_size(int first, int second) {
    check_vertex(first);
    check_vertex(second);
    assert(connected(first, second));
    make_root(first);
    access(second);
    return nodes[second].auxiliary_size;
  }

private:
  std::vector<int> splay_stack;

  void check_vertex(int vertex) const {
    assert(0 <= vertex && vertex < size());
  }

  bool is_auxiliary_root(int vertex) const {
    int parent = nodes[vertex].parent;
    return parent == -1 || (nodes[parent].child[0] != vertex &&
                            nodes[parent].child[1] != vertex);
  }

  int auxiliary_size(int vertex) const {
    return vertex == -1 ? 0 : nodes[vertex].auxiliary_size;
  }

  value_type forward_product(int vertex) const {
    return vertex == -1 ? Monoid::unit() : nodes[vertex].forward_product;
  }

  value_type backward_product(int vertex) const {
    return vertex == -1 ? Monoid::unit() : nodes[vertex].backward_product;
  }

  void pull(int vertex) {
    int left = nodes[vertex].child[0];
    int right = nodes[vertex].child[1];
    nodes[vertex].auxiliary_size =
        1 + auxiliary_size(left) + auxiliary_size(right);
    nodes[vertex].forward_product =
        Monoid::op(Monoid::op(forward_product(left), nodes[vertex].value),
                   forward_product(right));
    nodes[vertex].backward_product =
        Monoid::op(Monoid::op(backward_product(right), nodes[vertex].value),
                   backward_product(left));
  }

  void apply_reverse(int vertex) {
    if (vertex == -1) {
      return;
    }
    std::swap(nodes[vertex].child[0], nodes[vertex].child[1]);
    std::swap(nodes[vertex].forward_product,
              nodes[vertex].backward_product);
    nodes[vertex].reversed = !nodes[vertex].reversed;
  }

  void push(int vertex) {
    if (!nodes[vertex].reversed) {
      return;
    }
    apply_reverse(nodes[vertex].child[0]);
    apply_reverse(nodes[vertex].child[1]);
    nodes[vertex].reversed = false;
  }

  void rotate(int vertex) {
    int parent = nodes[vertex].parent;
    int grandparent = nodes[parent].parent;
    int direction = nodes[parent].child[1] == vertex;
    int middle = nodes[vertex].child[direction ^ 1];

    if (!is_auxiliary_root(parent)) {
      nodes[grandparent].child[nodes[grandparent].child[1] == parent] = vertex;
    }
    nodes[vertex].parent = grandparent;
    nodes[vertex].child[direction ^ 1] = parent;
    nodes[parent].parent = vertex;
    nodes[parent].child[direction] = middle;
    if (middle != -1) {
      nodes[middle].parent = parent;
    }
    pull(parent);
    pull(vertex);
  }

  void splay(int vertex) {
    splay_stack.clear();
    splay_stack.push_back(vertex);
    for (int current = vertex; !is_auxiliary_root(current);) {
      current = nodes[current].parent;
      splay_stack.push_back(current);
    }
    for (auto iterator = splay_stack.rbegin(); iterator != splay_stack.rend();
         ++iterator) {
      push(*iterator);
    }

    while (!is_auxiliary_root(vertex)) {
      int parent = nodes[vertex].parent;
      int grandparent = nodes[parent].parent;
      if (!is_auxiliary_root(parent)) {
        bool vertex_right = nodes[parent].child[1] == vertex;
        bool parent_right = nodes[grandparent].child[1] == parent;
        rotate(vertex_right == parent_right ? parent : vertex);
      }
      rotate(vertex);
    }
  }

  int access(int vertex) {
    int last = -1;
    for (int current = vertex; current != -1;) {
      splay(current);
      int path_parent = nodes[current].parent;
      nodes[current].child[1] = last;
      if (last != -1) {
        nodes[last].parent = current;
      }
      pull(current);
      last = current;
      current = path_parent;
    }
    splay(vertex);
    return last;
  }

  void make_root(int vertex) {
    access(vertex);
    apply_reverse(vertex);
  }

  int find_root(int vertex) {
    access(vertex);
    push(vertex);
    while (nodes[vertex].child[0] != -1) {
      vertex = nodes[vertex].child[0];
      push(vertex);
    }
    splay(vertex);
    return vertex;
  }
};

} // namespace noya