Skip to content

dynamic_tree_path_affine_sum.hpp

SECTIONGraph INCLUDEnoya/dynamic_tree_path_affine_sum.hpp

Maintain sums of edge-affine transforms from any chosen root. Every original edge is split by an edge-node carrying its affine map. A top tree cluster stores the affine transform along its boundary path, the sum of all values entering that path, and their count. Compress composes paths; rake adds independent branches. Preferred-path expose therefore propagates a point update to the complete rerooted aggregate in logarithmic time.

Verified by point_set_tree_path_composite_sum, point_set_tree_path_composite_sum_fixed_root.

在动态森林中连边断边、修改点仿射函数,并查询有向路径的函数复合。

Implementation

View on GitHub

#ifndef NOYA_DYNAMIC_TREE_PATH_AFFINE_SUM_HPP
#define NOYA_DYNAMIC_TREE_PATH_AFFINE_SUM_HPP 1

/// @complexity Time: O(n log n) construction and O(log n) amortized per
/// vertex update, edge update, or root query. Space: O(n).

#include "noya/dynamic_rerooting_top_tree.hpp"

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

namespace noya {

namespace dynamic_tree_path_affine_sum_internal {

template <class Mint> struct traits {
  struct path {
    Mint multiplier{};
    Mint addition{};
    Mint sum{};
    Mint count{};
  };
  struct point {
    Mint sum{};
    Mint count{};
  };
  struct info {
    bool is_vertex = true;
    Mint first{};
    Mint second{};
  };

  static path vertex(const info &item) {
    if (item.is_vertex) {
      return {Mint(1), Mint(0), item.first, Mint(1)};
    }
    return {item.first, item.second, Mint(0), Mint(0)};
  }

  static path compress(const path &parent, const path &child) {
    return {parent.multiplier * child.multiplier,
            parent.multiplier * child.addition + parent.addition,
            parent.sum + parent.multiplier * child.sum +
                parent.addition * child.count,
            parent.count + child.count};
  }

  static point rake(const point &first, const point &second) {
    return {first.sum + second.sum, first.count + second.count};
  }

  static point add_edge(const path &cluster) {
    return {cluster.sum, cluster.count};
  }

  static path add_vertex(const point &children, const info &item) {
    if (item.is_vertex) {
      return {Mint(1), Mint(0), children.sum + item.first,
              children.count + Mint(1)};
    }
    return {item.first, item.second,
            children.sum * item.first + children.count * item.second,
            children.count};
  }
};

} // namespace dynamic_tree_path_affine_sum_internal

/// @brief Maintain sums of edge-affine transforms from any chosen root.
/// Every original edge is split by an edge-node carrying its affine map. A top
/// tree cluster stores the affine transform along its boundary path, the sum
/// of all values entering that path, and their count. Compress composes paths;
/// rake adds independent branches. Preferred-path expose therefore propagates
/// a point update to the complete rerooted aggregate in logarithmic time.
template <class Mint> class dynamic_tree_path_affine_sum {
  using trait = dynamic_tree_path_affine_sum_internal::traits<Mint>;
  using path = typename trait::path;
  using point = typename trait::point;
  using info = typename trait::info;
  using dp_type =
      dynamic_rerooting_dp<path, point, info, trait::vertex, trait::compress,
                           trait::rake, trait::add_edge, trait::add_vertex>;

  int vertex_count;
  dp_type dp;

  static std::vector<info>
  make_info(const std::vector<Mint> &vertex_values,
            const std::vector<std::pair<Mint, Mint>> &edge_affine) {
    std::vector<info> result(vertex_values.size() + edge_affine.size());
    for (int vertex = 0; vertex < int(vertex_values.size()); vertex++) {
      result[vertex] = {true, vertex_values[vertex], Mint(0)};
    }
    for (int edge = 0; edge < int(edge_affine.size()); edge++) {
      result[vertex_values.size() + edge] =
          {false, edge_affine[edge].first, edge_affine[edge].second};
    }
    return result;
  }

public:
  dynamic_tree_path_affine_sum(
      const std::vector<Mint> &vertex_values,
      const std::vector<std::pair<int, int>> &edge_endpoints,
      const std::vector<std::pair<Mint, Mint>> &edge_affine)
      : vertex_count(int(vertex_values.size())),
        dp(make_info(vertex_values, edge_affine)) {
    assert(vertex_count >= 1);
    assert(int(edge_endpoints.size()) == vertex_count - 1);
    assert(edge_affine.size() == edge_endpoints.size());
    for (int edge = 0; edge < vertex_count - 1; edge++) {
      auto [first, second] = edge_endpoints[edge];
      assert(0 <= first && first < vertex_count && 0 <= second &&
             second < vertex_count);
      dp.link(vertex_count + edge, first);
      dp.link(vertex_count + edge, second);
    }
  }

  void set_vertex(int vertex, Mint value) {
    assert(0 <= vertex && vertex < vertex_count);
    dp.set_info(vertex, {true, value, Mint(0)});
  }

  void set_edge(int edge, Mint multiplier, Mint addition) {
    assert(0 <= edge && edge < vertex_count - 1);
    dp.set_info(vertex_count + edge,
                {false, multiplier, addition});
  }

  Mint query(int root) {
    assert(0 <= root && root < vertex_count);
    return dp.query(root).sum;
  }
};

} // namespace noya

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

/// @complexity Time: O(n log n) construction and O(log n) amortized per
/// vertex update, edge update, or root query. Space: O(n).

/// @complexity Time: O(log n) amortized per link, cut, point update, reroot,
/// or aggregate query. Space: O(n).

namespace noya {

namespace dynamic_rerooting_internal {

template <class Point, auto rake> class dashed_splay_tree {
public:
  struct node {
    node *left = nullptr;
    node *right = nullptr;
    node *parent = nullptr;
    Point key;
    Point sum;

    explicit node(const Point &value) : key(value), sum(value) {}
  };
  using node_pointer = node *;

private:
  static void update(node_pointer current) {
    current->sum = current->key;
    if (current->left) {
      current->sum = rake(current->sum, current->left->sum);
    }
    if (current->right) {
      current->sum = rake(current->sum, current->right->sum);
    }
  }

  static void rotate_right(node_pointer current) {
    node_pointer parent = current->parent;
    node_pointer grandparent = parent->parent;
    parent->left = current->right;
    if (current->right) {
      current->right->parent = parent;
    }
    current->right = parent;
    parent->parent = current;
    update(parent);
    update(current);
    current->parent = grandparent;
    if (grandparent) {
      if (grandparent->left == parent) {
        grandparent->left = current;
      }
      if (grandparent->right == parent) {
        grandparent->right = current;
      }
    }
  }

  static void rotate_left(node_pointer current) {
    node_pointer parent = current->parent;
    node_pointer grandparent = parent->parent;
    parent->right = current->left;
    if (current->left) {
      current->left->parent = parent;
    }
    current->left = parent;
    parent->parent = current;
    update(parent);
    update(current);
    current->parent = grandparent;
    if (grandparent) {
      if (grandparent->left == parent) {
        grandparent->left = current;
      }
      if (grandparent->right == parent) {
        grandparent->right = current;
      }
    }
  }

  static node_pointer rightmost(node_pointer current) {
    while (current->right) {
      current = current->right;
    }
    return current;
  }

public:
  static void splay(node_pointer current) {
    while (current->parent) {
      node_pointer parent = current->parent;
      if (!parent->parent) {
        if (parent->left == current) {
          rotate_right(current);
        } else {
          rotate_left(current);
        }
      } else {
        node_pointer grandparent = parent->parent;
        if (grandparent->left == parent) {
          if (parent->left == current) {
            rotate_right(parent);
            rotate_right(current);
          } else {
            rotate_left(current);
            rotate_right(current);
          }
        } else if (parent->right == current) {
          rotate_left(parent);
          rotate_left(current);
        } else {
          rotate_right(current);
          rotate_left(current);
        }
      }
    }
  }

  static node_pointer insert(node_pointer root, const Point &value) {
    if (!root) {
      return new node(value);
    }
    node_pointer last = rightmost(root);
    splay(last);
    node_pointer inserted = new node(value);
    inserted->parent = last;
    last->right = inserted;
    update(last);
    splay(inserted);
    return inserted;
  }

  static node_pointer erase(node_pointer current) {
    splay(current);
    node_pointer left = current->left;
    node_pointer right = current->right;
    delete current;
    if (!left) {
      if (right) {
        right->parent = nullptr;
      }
      return right;
    }
    if (!right) {
      left->parent = nullptr;
      return left;
    }
    left->parent = nullptr;
    node_pointer root = rightmost(left);
    splay(root);
    root->right = right;
    right->parent = root;
    update(root);
    return root;
  }
};

} // namespace dynamic_rerooting_internal

/// @brief Dynamic rerooting DP on a top tree represented by preferred paths.
/// `compress` joins consecutive path clusters, while commutative `rake`
/// combines light subtrees. `add_edge` changes a path cluster into a point
/// cluster and `add_vertex` attaches all light clusters to one path vertex.
/// Expose moves preferred edges between the path splay and the dashed-edge
/// splay, so both forward and reversed path aggregates stay current.
template <class Path, class Point, class Info, auto vertex, auto compress,
          auto rake, auto add_edge, auto add_vertex>
class dynamic_rerooting_top_tree {
  using dashed_tree =
      dynamic_rerooting_internal::dashed_splay_tree<Point, rake>;

  struct node {
    node *left = nullptr;
    node *right = nullptr;
    node *parent = nullptr;
    Info info;
    Path key{};
    Path forward{};
    Path backward{};
    typename dashed_tree::node_pointer light = nullptr;
    typename dashed_tree::node_pointer light_position = nullptr;
    bool reversed = false;

    explicit node(const Info &value) : info(value) {}

    bool is_auxiliary_root() const {
      return !parent || (parent->left != this && parent->right != this);
    }
  };

public:
  using node_pointer = node *;

private:
  static void toggle(node_pointer current) {
    std::swap(current->left, current->right);
    std::swap(current->forward, current->backward);
    current->reversed ^= true;
  }

  static void push(node_pointer current) {
    if (!current->reversed) {
      return;
    }
    if (current->left) {
      toggle(current->left);
    }
    if (current->right) {
      toggle(current->right);
    }
    current->reversed = false;
  }

  static void update(node_pointer current) {
    Path key = current->light
                   ? add_vertex(current->light->sum, current->info)
                   : vertex(current->info);
    Path forward = key;
    Path backward = key;
    if (current->left) {
      forward = compress(current->left->forward, forward);
      backward = compress(backward, current->left->backward);
    }
    if (current->right) {
      forward = compress(forward, current->right->forward);
      backward = compress(current->right->backward, backward);
    }
    current->key = key;
    current->forward = forward;
    current->backward = backward;
  }

  static void rotate_right(node_pointer current) {
    node_pointer parent = current->parent;
    node_pointer grandparent = parent->parent;
    push(parent);
    push(current);
    parent->left = current->right;
    if (current->right) {
      current->right->parent = parent;
    }
    current->right = parent;
    parent->parent = current;
    update(parent);
    update(current);
    current->parent = grandparent;
    if (grandparent) {
      if (grandparent->left == parent) {
        grandparent->left = current;
      }
      if (grandparent->right == parent) {
        grandparent->right = current;
      }
    }
  }

  static void rotate_left(node_pointer current) {
    node_pointer parent = current->parent;
    node_pointer grandparent = parent->parent;
    push(parent);
    push(current);
    parent->right = current->left;
    if (current->left) {
      current->left->parent = parent;
    }
    current->left = parent;
    parent->parent = current;
    update(parent);
    update(current);
    current->parent = grandparent;
    if (grandparent) {
      if (grandparent->left == parent) {
        grandparent->left = current;
      }
      if (grandparent->right == parent) {
        grandparent->right = current;
      }
    }
  }

  static void splay(node_pointer current) {
    push(current);
    {
      node_pointer root = current;
      while (!root->is_auxiliary_root()) {
        root = root->parent;
      }
      current->light_position = root->light_position;
      if (current != root) {
        root->light_position = nullptr;
      }
    }
    while (!current->is_auxiliary_root()) {
      node_pointer parent = current->parent;
      if (parent->is_auxiliary_root()) {
        push(parent);
        push(current);
        if (parent->left == current) {
          rotate_right(current);
        } else {
          rotate_left(current);
        }
      } else {
        node_pointer grandparent = parent->parent;
        push(grandparent);
        push(parent);
        push(current);
        if (grandparent->left == parent) {
          if (parent->left == current) {
            rotate_right(parent);
            rotate_right(current);
          } else {
            rotate_left(current);
            rotate_right(current);
          }
        } else if (parent->right == current) {
          rotate_left(parent);
          rotate_left(current);
        } else {
          rotate_right(current);
          rotate_left(current);
        }
      }
    }
  }

  static node_pointer expose(node_pointer current) {
    node_pointer preferred = nullptr;
    for (node_pointer ancestor = current; ancestor; ancestor = ancestor->parent) {
      splay(ancestor);
      if (ancestor->right) {
        ancestor->light =
            dashed_tree::insert(ancestor->light,
                                add_edge(ancestor->right->forward));
        ancestor->right->light_position = ancestor->light;
      }
      ancestor->right = preferred;
      if (preferred) {
        dashed_tree::splay(preferred->light_position);
        push(preferred);
        ancestor->light = dashed_tree::erase(preferred->light_position);
      }
      update(ancestor);
      preferred = ancestor;
    }
    splay(current);
    return preferred;
  }

public:
  node_pointer make_node(const Info &info) {
    node_pointer result = new node(info);
    update(result);
    return result;
  }

  void evert(node_pointer current) {
    expose(current);
    toggle(current);
    push(current);
  }

  void link(node_pointer child, node_pointer parent) {
    expose(parent);
    expose(child);
    child->parent = parent;
    parent->right = child;
    update(parent);
  }

  void cut(node_pointer child) {
    expose(child);
    node_pointer parent = child->left;
    assert(parent);
    child->left = nullptr;
    parent->parent = nullptr;
    update(child);
  }

  void set_info(node_pointer current, const Info &info) {
    expose(current);
    current->info = info;
    update(current);
  }

  Info get_info(node_pointer current) const { return current->info; }

  Path query(node_pointer root) {
    evert(root);
    return root->forward;
  }

  Path query_subtree(node_pointer root, node_pointer subtree_root) {
    evert(root);
    expose(subtree_root);
    node_pointer prefix = subtree_root->left;
    subtree_root->left = nullptr;
    update(subtree_root);
    Path result = subtree_root->forward;
    subtree_root->left = prefix;
    update(subtree_root);
    return result;
  }
};

/// @brief Index-based wrapper around `dynamic_rerooting_top_tree`.
template <class Path, class Point, class Info, auto vertex, auto compress,
          auto rake, auto add_edge, auto add_vertex>
class dynamic_rerooting_dp {
  using top_tree_type = dynamic_rerooting_top_tree<
      Path, Point, Info, vertex, compress, rake, add_edge, add_vertex>;
  top_tree_type tree;
  std::vector<typename top_tree_type::node_pointer> nodes;

public:
  explicit dynamic_rerooting_dp(const std::vector<Info> &info)
      : nodes(info.size()) {
    for (int i = 0; i < int(info.size()); i++) {
      nodes[i] = tree.make_node(info[i]);
    }
  }

  void link(int first, int second) {
    tree.evert(nodes[first]);
    tree.link(nodes[first], nodes[second]);
  }

  void cut(int first, int second) {
    tree.evert(nodes[first]);
    tree.cut(nodes[second]);
  }

  void set_info(int index, const Info &info) {
    tree.set_info(nodes[index], info);
  }

  Info get_info(int index) const { return tree.get_info(nodes[index]); }

  Path query(int root) { return tree.query(nodes[root]); }

  Path query_subtree(int root, int subtree_root) {
    return tree.query_subtree(nodes[root], nodes[subtree_root]);
  }
};

} // namespace noya

namespace noya {

namespace dynamic_tree_path_affine_sum_internal {

template <class Mint> struct traits {
  struct path {
    Mint multiplier{};
    Mint addition{};
    Mint sum{};
    Mint count{};
  };
  struct point {
    Mint sum{};
    Mint count{};
  };
  struct info {
    bool is_vertex = true;
    Mint first{};
    Mint second{};
  };

  static path vertex(const info &item) {
    if (item.is_vertex) {
      return {Mint(1), Mint(0), item.first, Mint(1)};
    }
    return {item.first, item.second, Mint(0), Mint(0)};
  }

  static path compress(const path &parent, const path &child) {
    return {parent.multiplier * child.multiplier,
            parent.multiplier * child.addition + parent.addition,
            parent.sum + parent.multiplier * child.sum +
                parent.addition * child.count,
            parent.count + child.count};
  }

  static point rake(const point &first, const point &second) {
    return {first.sum + second.sum, first.count + second.count};
  }

  static point add_edge(const path &cluster) {
    return {cluster.sum, cluster.count};
  }

  static path add_vertex(const point &children, const info &item) {
    if (item.is_vertex) {
      return {Mint(1), Mint(0), children.sum + item.first,
              children.count + Mint(1)};
    }
    return {item.first, item.second,
            children.sum * item.first + children.count * item.second,
            children.count};
  }
};

} // namespace dynamic_tree_path_affine_sum_internal

/// @brief Maintain sums of edge-affine transforms from any chosen root.
/// Every original edge is split by an edge-node carrying its affine map. A top
/// tree cluster stores the affine transform along its boundary path, the sum
/// of all values entering that path, and their count. Compress composes paths;
/// rake adds independent branches. Preferred-path expose therefore propagates
/// a point update to the complete rerooted aggregate in logarithmic time.
template <class Mint> class dynamic_tree_path_affine_sum {
  using trait = dynamic_tree_path_affine_sum_internal::traits<Mint>;
  using path = typename trait::path;
  using point = typename trait::point;
  using info = typename trait::info;
  using dp_type =
      dynamic_rerooting_dp<path, point, info, trait::vertex, trait::compress,
                           trait::rake, trait::add_edge, trait::add_vertex>;

  int vertex_count;
  dp_type dp;

  static std::vector<info>
  make_info(const std::vector<Mint> &vertex_values,
            const std::vector<std::pair<Mint, Mint>> &edge_affine) {
    std::vector<info> result(vertex_values.size() + edge_affine.size());
    for (int vertex = 0; vertex < int(vertex_values.size()); vertex++) {
      result[vertex] = {true, vertex_values[vertex], Mint(0)};
    }
    for (int edge = 0; edge < int(edge_affine.size()); edge++) {
      result[vertex_values.size() + edge] =
          {false, edge_affine[edge].first, edge_affine[edge].second};
    }
    return result;
  }

public:
  dynamic_tree_path_affine_sum(
      const std::vector<Mint> &vertex_values,
      const std::vector<std::pair<int, int>> &edge_endpoints,
      const std::vector<std::pair<Mint, Mint>> &edge_affine)
      : vertex_count(int(vertex_values.size())),
        dp(make_info(vertex_values, edge_affine)) {
    assert(vertex_count >= 1);
    assert(int(edge_endpoints.size()) == vertex_count - 1);
    assert(edge_affine.size() == edge_endpoints.size());
    for (int edge = 0; edge < vertex_count - 1; edge++) {
      auto [first, second] = edge_endpoints[edge];
      assert(0 <= first && first < vertex_count && 0 <= second &&
             second < vertex_count);
      dp.link(vertex_count + edge, first);
      dp.link(vertex_count + edge, second);
    }
  }

  void set_vertex(int vertex, Mint value) {
    assert(0 <= vertex && vertex < vertex_count);
    dp.set_info(vertex, {true, value, Mint(0)});
  }

  void set_edge(int edge, Mint multiplier, Mint addition) {
    assert(0 <= edge && edge < vertex_count - 1);
    dp.set_info(vertex_count + edge,
                {false, multiplier, addition});
  }

  Mint query(int root) {
    assert(0 <= root && root < vertex_count);
    return dp.query(root).sum;
  }
};

} // namespace noya