Skip to content

dynamic_rerooting_top_tree.hpp

SECTIONGraph INCLUDEnoya/dynamic_rerooting_top_tree.hpp

用 Top Tree 在动态森林中维护可换根的全树 DP;适合连边断边后仍需查询整棵树答案。

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

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include

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

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

namespace noya {

namespace dynamic_rerooting_internal {

template <class Point, auto rk> class dashed_splay_tree {
public:
  struct node {
    node *l = nullptr;
    node *r = nullptr;
    node *fa = nullptr;
    Point key;
    Point sum;

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

private:
  static void update(node_pointer cur) {
    cur->sum = cur->key;
    if (cur->l) {
      cur->sum = rk(cur->sum, cur->l->sum);
    }
    if (cur->r) {
      cur->sum = rk(cur->sum, cur->r->sum);
    }
  }

  static void rotate_right(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    fa->l = cur->r;
    if (cur->r) {
      cur->r->fa = fa;
    }
    cur->r = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void rotate_left(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    fa->r = cur->l;
    if (cur->l) {
      cur->l->fa = fa;
    }
    cur->l = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static node_pointer rightmost(node_pointer cur) {
    while (cur->r) {
      cur = cur->r;
    }
    return cur;
  }

public:
  static void splay(node_pointer cur) {
    while (cur->fa) {
      node_pointer fa = cur->fa;
      if (!fa->fa) {
        if (fa->l == cur) {
          rotate_right(cur);
        } else {
          rotate_left(cur);
        }
      } else {
        node_pointer gf = fa->fa;
        if (gf->l == fa) {
          if (fa->l == cur) {
            rotate_right(fa);
            rotate_right(cur);
          } else {
            rotate_left(cur);
            rotate_right(cur);
          }
        } else if (fa->r == cur) {
          rotate_left(fa);
          rotate_left(cur);
        } else {
          rotate_right(cur);
          rotate_left(cur);
        }
      }
    }
  }

  static node_pointer insert(node_pointer rt, const Point &val) {
    if (!rt) {
      return new node(val);
    }
    node_pointer lst = rightmost(rt);
    splay(lst);
    node_pointer ins = new node(val);
    ins->fa = lst;
    lst->r = ins;
    update(lst);
    splay(ins);
    return ins;
  }

  static node_pointer erase(node_pointer cur) {
    splay(cur);
    node_pointer l = cur->l;
    node_pointer r = cur->r;
    delete cur;
    if (!l) {
      if (r) {
        r->fa = nullptr;
      }
      return r;
    }
    if (!r) {
      l->fa = nullptr;
      return l;
    }
    l->fa = nullptr;
    node_pointer rt = rightmost(l);
    splay(rt);
    rt->r = r;
    r->fa = rt;
    update(rt);
    return rt;
  }
};

} // namespace dynamic_rerooting_internal

/// @brief Dynamic rerooting DP on a top tree represented by preferred paths.
/// `cmp` joins consecutive path clusters, while commutative `rk`
/// combines light subtrees. `ae` changes a path cluster into a point
/// cluster and `av` 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 vtx, auto cmp, auto rk,
          auto ae, auto av>
class dynamic_rerooting_top_tree {
  using dashed_tree = dynamic_rerooting_internal::dashed_splay_tree<Point, rk>;

  struct node {
    node *l = nullptr;
    node *r = nullptr;
    node *fa = nullptr;
    Info inf;
    Path key{};
    Path fwd{};
    Path bwd{};
    typename dashed_tree::node_pointer lt = nullptr;
    typename dashed_tree::node_pointer lp = nullptr;
    bool rev = false;

    explicit node(const Info &val) : inf(val) {}

    bool is_auxiliary_root() const {
      return !fa || (fa->l != this && fa->r != this);
    }
  };

public:
  using node_pointer = node *;

private:
  static void toggle(node_pointer cur) {
    std::swap(cur->l, cur->r);
    std::swap(cur->fwd, cur->bwd);
    cur->rev ^= true;
  }

  static void push(node_pointer cur) {
    if (!cur->rev) {
      return;
    }
    if (cur->l) {
      toggle(cur->l);
    }
    if (cur->r) {
      toggle(cur->r);
    }
    cur->rev = false;
  }

  static void update(node_pointer cur) {
    Path key = cur->lt ? av(cur->lt->sum, cur->inf) : vtx(cur->inf);
    Path fwd = key;
    Path bwd = key;
    if (cur->l) {
      fwd = cmp(cur->l->fwd, fwd);
      bwd = cmp(bwd, cur->l->bwd);
    }
    if (cur->r) {
      fwd = cmp(fwd, cur->r->fwd);
      bwd = cmp(cur->r->bwd, bwd);
    }
    cur->key = key;
    cur->fwd = fwd;
    cur->bwd = bwd;
  }

  static void rotate_right(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    push(fa);
    push(cur);
    fa->l = cur->r;
    if (cur->r) {
      cur->r->fa = fa;
    }
    cur->r = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void rotate_left(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    push(fa);
    push(cur);
    fa->r = cur->l;
    if (cur->l) {
      cur->l->fa = fa;
    }
    cur->l = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void splay(node_pointer cur) {
    push(cur);
    {
      node_pointer rt = cur;
      while (!rt->is_auxiliary_root()) {
        rt = rt->fa;
      }
      cur->lp = rt->lp;
      if (cur != rt) {
        rt->lp = nullptr;
      }
    }
    while (!cur->is_auxiliary_root()) {
      node_pointer fa = cur->fa;
      if (fa->is_auxiliary_root()) {
        push(fa);
        push(cur);
        if (fa->l == cur) {
          rotate_right(cur);
        } else {
          rotate_left(cur);
        }
      } else {
        node_pointer gf = fa->fa;
        push(gf);
        push(fa);
        push(cur);
        if (gf->l == fa) {
          if (fa->l == cur) {
            rotate_right(fa);
            rotate_right(cur);
          } else {
            rotate_left(cur);
            rotate_right(cur);
          }
        } else if (fa->r == cur) {
          rotate_left(fa);
          rotate_left(cur);
        } else {
          rotate_right(cur);
          rotate_left(cur);
        }
      }
    }
  }

  static node_pointer expose(node_pointer cur) {
    node_pointer pre = nullptr;
    for (node_pointer anc = cur; anc; anc = anc->fa) {
      splay(anc);
      if (anc->r) {
        anc->lt = dashed_tree::insert(anc->lt, ae(anc->r->fwd));
        anc->r->lp = anc->lt;
      }
      anc->r = pre;
      if (pre) {
        dashed_tree::splay(pre->lp);
        push(pre);
        anc->lt = dashed_tree::erase(pre->lp);
      }
      update(anc);
      pre = anc;
    }
    splay(cur);
    return pre;
  }

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

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

  void link(node_pointer v, node_pointer fa) {
    expose(fa);
    expose(v);
    v->fa = fa;
    fa->r = v;
    update(fa);
  }

  void cut(node_pointer v) {
    expose(v);
    node_pointer fa = v->l;
    assert(fa);
    v->l = nullptr;
    fa->fa = nullptr;
    update(v);
  }

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

  Info get_info(node_pointer cur) const { return cur->inf; }

  Path query(node_pointer rt) {
    evert(rt);
    return rt->fwd;
  }

  Path query_subtree(node_pointer rt, node_pointer rot) {
    evert(rt);
    expose(rot);
    node_pointer prv = rot->l;
    rot->l = nullptr;
    update(rot);
    Path res = rot->fwd;
    rot->l = prv;
    update(rot);
    return res;
  }
};

/// @brief Index-based wrapper around `dynamic_rerooting_top_tree`.
template <class Path, class Point, class Info, auto vtx, auto cmp, auto rk,
          auto ae, auto av>
class dynamic_rerooting_dp {
  using top_tree_type =
      dynamic_rerooting_top_tree<Path, Point, Info, vtx, cmp, rk, ae, av>;
  top_tree_type tr;
  std::vector<typename top_tree_type::node_pointer> nd;

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

  void link(int a, int b) {
    tr.evert(nd[a]);
    tr.link(nd[a], nd[b]);
  }

  void cut(int a, int b) {
    tr.evert(nd[a]);
    tr.cut(nd[b]);
  }

  void set_info(int idx, const Info &inf) { tr.set_info(nd[idx], inf); }

  Info get_info(int idx) const { return tr.get_info(nd[idx]); }

  Path query(int rt) { return tr.query(nd[rt]); }

  Path query_subtree(int rt, int rot) {
    return tr.query_subtree(nd[rt], nd[rot]);
  }
};

} // namespace noya
#ifndef NOYA_DYNAMIC_REROOTING_TOP_TREE_HPP
#define NOYA_DYNAMIC_REROOTING_TOP_TREE_HPP 1

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

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

namespace noya {

namespace dynamic_rerooting_internal {

template <class Point, auto rk> class dashed_splay_tree {
public:
  struct node {
    node *l = nullptr;
    node *r = nullptr;
    node *fa = nullptr;
    Point key;
    Point sum;

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

private:
  static void update(node_pointer cur) {
    cur->sum = cur->key;
    if (cur->l) {
      cur->sum = rk(cur->sum, cur->l->sum);
    }
    if (cur->r) {
      cur->sum = rk(cur->sum, cur->r->sum);
    }
  }

  static void rotate_right(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    fa->l = cur->r;
    if (cur->r) {
      cur->r->fa = fa;
    }
    cur->r = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void rotate_left(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    fa->r = cur->l;
    if (cur->l) {
      cur->l->fa = fa;
    }
    cur->l = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static node_pointer rightmost(node_pointer cur) {
    while (cur->r) {
      cur = cur->r;
    }
    return cur;
  }

public:
  static void splay(node_pointer cur) {
    while (cur->fa) {
      node_pointer fa = cur->fa;
      if (!fa->fa) {
        if (fa->l == cur) {
          rotate_right(cur);
        } else {
          rotate_left(cur);
        }
      } else {
        node_pointer gf = fa->fa;
        if (gf->l == fa) {
          if (fa->l == cur) {
            rotate_right(fa);
            rotate_right(cur);
          } else {
            rotate_left(cur);
            rotate_right(cur);
          }
        } else if (fa->r == cur) {
          rotate_left(fa);
          rotate_left(cur);
        } else {
          rotate_right(cur);
          rotate_left(cur);
        }
      }
    }
  }

  static node_pointer insert(node_pointer rt, const Point &val) {
    if (!rt) {
      return new node(val);
    }
    node_pointer lst = rightmost(rt);
    splay(lst);
    node_pointer ins = new node(val);
    ins->fa = lst;
    lst->r = ins;
    update(lst);
    splay(ins);
    return ins;
  }

  static node_pointer erase(node_pointer cur) {
    splay(cur);
    node_pointer l = cur->l;
    node_pointer r = cur->r;
    delete cur;
    if (!l) {
      if (r) {
        r->fa = nullptr;
      }
      return r;
    }
    if (!r) {
      l->fa = nullptr;
      return l;
    }
    l->fa = nullptr;
    node_pointer rt = rightmost(l);
    splay(rt);
    rt->r = r;
    r->fa = rt;
    update(rt);
    return rt;
  }
};

} // namespace dynamic_rerooting_internal

/// @brief Dynamic rerooting DP on a top tree represented by preferred paths.
/// `cmp` joins consecutive path clusters, while commutative `rk`
/// combines light subtrees. `ae` changes a path cluster into a point
/// cluster and `av` 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 vtx, auto cmp, auto rk,
          auto ae, auto av>
class dynamic_rerooting_top_tree {
  using dashed_tree = dynamic_rerooting_internal::dashed_splay_tree<Point, rk>;

  struct node {
    node *l = nullptr;
    node *r = nullptr;
    node *fa = nullptr;
    Info inf;
    Path key{};
    Path fwd{};
    Path bwd{};
    typename dashed_tree::node_pointer lt = nullptr;
    typename dashed_tree::node_pointer lp = nullptr;
    bool rev = false;

    explicit node(const Info &val) : inf(val) {}

    bool is_auxiliary_root() const {
      return !fa || (fa->l != this && fa->r != this);
    }
  };

public:
  using node_pointer = node *;

private:
  static void toggle(node_pointer cur) {
    std::swap(cur->l, cur->r);
    std::swap(cur->fwd, cur->bwd);
    cur->rev ^= true;
  }

  static void push(node_pointer cur) {
    if (!cur->rev) {
      return;
    }
    if (cur->l) {
      toggle(cur->l);
    }
    if (cur->r) {
      toggle(cur->r);
    }
    cur->rev = false;
  }

  static void update(node_pointer cur) {
    Path key = cur->lt ? av(cur->lt->sum, cur->inf) : vtx(cur->inf);
    Path fwd = key;
    Path bwd = key;
    if (cur->l) {
      fwd = cmp(cur->l->fwd, fwd);
      bwd = cmp(bwd, cur->l->bwd);
    }
    if (cur->r) {
      fwd = cmp(fwd, cur->r->fwd);
      bwd = cmp(cur->r->bwd, bwd);
    }
    cur->key = key;
    cur->fwd = fwd;
    cur->bwd = bwd;
  }

  static void rotate_right(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    push(fa);
    push(cur);
    fa->l = cur->r;
    if (cur->r) {
      cur->r->fa = fa;
    }
    cur->r = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void rotate_left(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    push(fa);
    push(cur);
    fa->r = cur->l;
    if (cur->l) {
      cur->l->fa = fa;
    }
    cur->l = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void splay(node_pointer cur) {
    push(cur);
    {
      node_pointer rt = cur;
      while (!rt->is_auxiliary_root()) {
        rt = rt->fa;
      }
      cur->lp = rt->lp;
      if (cur != rt) {
        rt->lp = nullptr;
      }
    }
    while (!cur->is_auxiliary_root()) {
      node_pointer fa = cur->fa;
      if (fa->is_auxiliary_root()) {
        push(fa);
        push(cur);
        if (fa->l == cur) {
          rotate_right(cur);
        } else {
          rotate_left(cur);
        }
      } else {
        node_pointer gf = fa->fa;
        push(gf);
        push(fa);
        push(cur);
        if (gf->l == fa) {
          if (fa->l == cur) {
            rotate_right(fa);
            rotate_right(cur);
          } else {
            rotate_left(cur);
            rotate_right(cur);
          }
        } else if (fa->r == cur) {
          rotate_left(fa);
          rotate_left(cur);
        } else {
          rotate_right(cur);
          rotate_left(cur);
        }
      }
    }
  }

  static node_pointer expose(node_pointer cur) {
    node_pointer pre = nullptr;
    for (node_pointer anc = cur; anc; anc = anc->fa) {
      splay(anc);
      if (anc->r) {
        anc->lt = dashed_tree::insert(anc->lt, ae(anc->r->fwd));
        anc->r->lp = anc->lt;
      }
      anc->r = pre;
      if (pre) {
        dashed_tree::splay(pre->lp);
        push(pre);
        anc->lt = dashed_tree::erase(pre->lp);
      }
      update(anc);
      pre = anc;
    }
    splay(cur);
    return pre;
  }

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

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

  void link(node_pointer v, node_pointer fa) {
    expose(fa);
    expose(v);
    v->fa = fa;
    fa->r = v;
    update(fa);
  }

  void cut(node_pointer v) {
    expose(v);
    node_pointer fa = v->l;
    assert(fa);
    v->l = nullptr;
    fa->fa = nullptr;
    update(v);
  }

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

  Info get_info(node_pointer cur) const { return cur->inf; }

  Path query(node_pointer rt) {
    evert(rt);
    return rt->fwd;
  }

  Path query_subtree(node_pointer rt, node_pointer rot) {
    evert(rt);
    expose(rot);
    node_pointer prv = rot->l;
    rot->l = nullptr;
    update(rot);
    Path res = rot->fwd;
    rot->l = prv;
    update(rot);
    return res;
  }
};

/// @brief Index-based wrapper around `dynamic_rerooting_top_tree`.
template <class Path, class Point, class Info, auto vtx, auto cmp, auto rk,
          auto ae, auto av>
class dynamic_rerooting_dp {
  using top_tree_type =
      dynamic_rerooting_top_tree<Path, Point, Info, vtx, cmp, rk, ae, av>;
  top_tree_type tr;
  std::vector<typename top_tree_type::node_pointer> nd;

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

  void link(int a, int b) {
    tr.evert(nd[a]);
    tr.link(nd[a], nd[b]);
  }

  void cut(int a, int b) {
    tr.evert(nd[a]);
    tr.cut(nd[b]);
  }

  void set_info(int idx, const Info &inf) { tr.set_info(nd[idx], inf); }

  Info get_info(int idx) const { return tr.get_info(nd[idx]); }

  Path query(int rt) { return tr.query(nd[rt]); }

  Path query_subtree(int rt, int rot) {
    return tr.query_subtree(nd[rt], nd[rot]);
  }
};

} // namespace noya

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

/// @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 rk> class dashed_splay_tree {
public:
  struct node {
    node *l = nullptr;
    node *r = nullptr;
    node *fa = nullptr;
    Point key;
    Point sum;

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

private:
  static void update(node_pointer cur) {
    cur->sum = cur->key;
    if (cur->l) {
      cur->sum = rk(cur->sum, cur->l->sum);
    }
    if (cur->r) {
      cur->sum = rk(cur->sum, cur->r->sum);
    }
  }

  static void rotate_right(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    fa->l = cur->r;
    if (cur->r) {
      cur->r->fa = fa;
    }
    cur->r = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void rotate_left(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    fa->r = cur->l;
    if (cur->l) {
      cur->l->fa = fa;
    }
    cur->l = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static node_pointer rightmost(node_pointer cur) {
    while (cur->r) {
      cur = cur->r;
    }
    return cur;
  }

public:
  static void splay(node_pointer cur) {
    while (cur->fa) {
      node_pointer fa = cur->fa;
      if (!fa->fa) {
        if (fa->l == cur) {
          rotate_right(cur);
        } else {
          rotate_left(cur);
        }
      } else {
        node_pointer gf = fa->fa;
        if (gf->l == fa) {
          if (fa->l == cur) {
            rotate_right(fa);
            rotate_right(cur);
          } else {
            rotate_left(cur);
            rotate_right(cur);
          }
        } else if (fa->r == cur) {
          rotate_left(fa);
          rotate_left(cur);
        } else {
          rotate_right(cur);
          rotate_left(cur);
        }
      }
    }
  }

  static node_pointer insert(node_pointer rt, const Point &val) {
    if (!rt) {
      return new node(val);
    }
    node_pointer lst = rightmost(rt);
    splay(lst);
    node_pointer ins = new node(val);
    ins->fa = lst;
    lst->r = ins;
    update(lst);
    splay(ins);
    return ins;
  }

  static node_pointer erase(node_pointer cur) {
    splay(cur);
    node_pointer l = cur->l;
    node_pointer r = cur->r;
    delete cur;
    if (!l) {
      if (r) {
        r->fa = nullptr;
      }
      return r;
    }
    if (!r) {
      l->fa = nullptr;
      return l;
    }
    l->fa = nullptr;
    node_pointer rt = rightmost(l);
    splay(rt);
    rt->r = r;
    r->fa = rt;
    update(rt);
    return rt;
  }
};

} // namespace dynamic_rerooting_internal

/// @brief Dynamic rerooting DP on a top tree represented by preferred paths.
/// `cmp` joins consecutive path clusters, while commutative `rk`
/// combines light subtrees. `ae` changes a path cluster into a point
/// cluster and `av` 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 vtx, auto cmp, auto rk,
          auto ae, auto av>
class dynamic_rerooting_top_tree {
  using dashed_tree = dynamic_rerooting_internal::dashed_splay_tree<Point, rk>;

  struct node {
    node *l = nullptr;
    node *r = nullptr;
    node *fa = nullptr;
    Info inf;
    Path key{};
    Path fwd{};
    Path bwd{};
    typename dashed_tree::node_pointer lt = nullptr;
    typename dashed_tree::node_pointer lp = nullptr;
    bool rev = false;

    explicit node(const Info &val) : inf(val) {}

    bool is_auxiliary_root() const {
      return !fa || (fa->l != this && fa->r != this);
    }
  };

public:
  using node_pointer = node *;

private:
  static void toggle(node_pointer cur) {
    std::swap(cur->l, cur->r);
    std::swap(cur->fwd, cur->bwd);
    cur->rev ^= true;
  }

  static void push(node_pointer cur) {
    if (!cur->rev) {
      return;
    }
    if (cur->l) {
      toggle(cur->l);
    }
    if (cur->r) {
      toggle(cur->r);
    }
    cur->rev = false;
  }

  static void update(node_pointer cur) {
    Path key = cur->lt ? av(cur->lt->sum, cur->inf) : vtx(cur->inf);
    Path fwd = key;
    Path bwd = key;
    if (cur->l) {
      fwd = cmp(cur->l->fwd, fwd);
      bwd = cmp(bwd, cur->l->bwd);
    }
    if (cur->r) {
      fwd = cmp(fwd, cur->r->fwd);
      bwd = cmp(cur->r->bwd, bwd);
    }
    cur->key = key;
    cur->fwd = fwd;
    cur->bwd = bwd;
  }

  static void rotate_right(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    push(fa);
    push(cur);
    fa->l = cur->r;
    if (cur->r) {
      cur->r->fa = fa;
    }
    cur->r = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void rotate_left(node_pointer cur) {
    node_pointer fa = cur->fa;
    node_pointer gf = fa->fa;
    push(fa);
    push(cur);
    fa->r = cur->l;
    if (cur->l) {
      cur->l->fa = fa;
    }
    cur->l = fa;
    fa->fa = cur;
    update(fa);
    update(cur);
    cur->fa = gf;
    if (gf) {
      if (gf->l == fa) {
        gf->l = cur;
      }
      if (gf->r == fa) {
        gf->r = cur;
      }
    }
  }

  static void splay(node_pointer cur) {
    push(cur);
    {
      node_pointer rt = cur;
      while (!rt->is_auxiliary_root()) {
        rt = rt->fa;
      }
      cur->lp = rt->lp;
      if (cur != rt) {
        rt->lp = nullptr;
      }
    }
    while (!cur->is_auxiliary_root()) {
      node_pointer fa = cur->fa;
      if (fa->is_auxiliary_root()) {
        push(fa);
        push(cur);
        if (fa->l == cur) {
          rotate_right(cur);
        } else {
          rotate_left(cur);
        }
      } else {
        node_pointer gf = fa->fa;
        push(gf);
        push(fa);
        push(cur);
        if (gf->l == fa) {
          if (fa->l == cur) {
            rotate_right(fa);
            rotate_right(cur);
          } else {
            rotate_left(cur);
            rotate_right(cur);
          }
        } else if (fa->r == cur) {
          rotate_left(fa);
          rotate_left(cur);
        } else {
          rotate_right(cur);
          rotate_left(cur);
        }
      }
    }
  }

  static node_pointer expose(node_pointer cur) {
    node_pointer pre = nullptr;
    for (node_pointer anc = cur; anc; anc = anc->fa) {
      splay(anc);
      if (anc->r) {
        anc->lt = dashed_tree::insert(anc->lt, ae(anc->r->fwd));
        anc->r->lp = anc->lt;
      }
      anc->r = pre;
      if (pre) {
        dashed_tree::splay(pre->lp);
        push(pre);
        anc->lt = dashed_tree::erase(pre->lp);
      }
      update(anc);
      pre = anc;
    }
    splay(cur);
    return pre;
  }

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

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

  void link(node_pointer v, node_pointer fa) {
    expose(fa);
    expose(v);
    v->fa = fa;
    fa->r = v;
    update(fa);
  }

  void cut(node_pointer v) {
    expose(v);
    node_pointer fa = v->l;
    assert(fa);
    v->l = nullptr;
    fa->fa = nullptr;
    update(v);
  }

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

  Info get_info(node_pointer cur) const { return cur->inf; }

  Path query(node_pointer rt) {
    evert(rt);
    return rt->fwd;
  }

  Path query_subtree(node_pointer rt, node_pointer rot) {
    evert(rt);
    expose(rot);
    node_pointer prv = rot->l;
    rot->l = nullptr;
    update(rot);
    Path res = rot->fwd;
    rot->l = prv;
    update(rot);
    return res;
  }
};

/// @brief Index-based wrapper around `dynamic_rerooting_top_tree`.
template <class Path, class Point, class Info, auto vtx, auto cmp, auto rk,
          auto ae, auto av>
class dynamic_rerooting_dp {
  using top_tree_type =
      dynamic_rerooting_top_tree<Path, Point, Info, vtx, cmp, rk, ae, av>;
  top_tree_type tr;
  std::vector<typename top_tree_type::node_pointer> nd;

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

  void link(int a, int b) {
    tr.evert(nd[a]);
    tr.link(nd[a], nd[b]);
  }

  void cut(int a, int b) {
    tr.evert(nd[a]);
    tr.cut(nd[b]);
  }

  void set_info(int idx, const Info &inf) { tr.set_info(nd[idx], inf); }

  Info get_info(int idx) const { return tr.get_info(nd[idx]); }

  Path query(int rt) { return tr.query(nd[rt]); }

  Path query_subtree(int rt, int rot) {
    return tr.query_subtree(nd[rt], nd[rot]);
  }
};

} // namespace noya