Skip to content

dynamic_tree_subtree.hpp

SECTIONGraph INCLUDEnoya/graph/dynamic_tree_subtree.hpp

可连边断边的森林中,按相邻边指定一侧做子树加与求和;直接暴露目标侧,省去子树操作中的拆边接边。

Complexity: Time: \(O(n)\) construction; amortized \(O(\log (n + 1))\) per link, cut, vertex update or directed-subtree operation; \(O(1)\) size. Space: \(O(n)\).

AC 记录:dynamic_tree_subtree_add_subtree_sum, dynamic_tree_vertex_add_subtree_sum。

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n) construction; amortized O(log(n+1)) per link,
/// cut, vertex update or directed-subtree operation; O(1) size. Space: O(n).

#include <cassert>
#include <cstddef>
#include <limits>
#include <vector>

namespace noya {

/// @brief Link-Cut Tree augmented with virtual-child size and sum. Access
/// moves preferred children into or out of the virtual aggregate; each node
/// remembers how much of its represented parent's lazy addition it has
/// already received, so subtree additions remain valid across preferred-path
/// changes. Vertices are [0,n), where n must fit int. Empty forests are valid.
/// link(a,b) requires different components; cut(a,b) requires an existing edge.
/// subtree_add/sum(u,p) require adjacent vertices and act on u's side of that
/// edge; p is not an arbitrary root. Values and deltas may be negative. All
/// sums, products and accumulated lazy-tag arithmetic must fit long long.
/// Nodes are owned by this object; copy/move are disabled to preserve pointers.
/// All tree operations and cleanup are iterative.
class dynamic_tree_subtree {
  struct node {
    node *fa = nullptr;
    node *v[2] = {nullptr, nullptr};
    long long val = 0;
    int siz = 0;
    int vsz = 0;
    long long sum = 0;
    long long vs = 0;
    long long add = 0;
    long long pas = 0;
    bool rev = false;

    void apply_add(long long dlt) {
      val += dlt;
      sum += static_cast<long long>(siz) * dlt;
      vs += static_cast<long long>(vsz) * dlt;
      add += dlt;
    }

    void push(node *nil) {
      // Most exposes carry no addition; avoid touching every aggregate then.
      if (fa != nil && pas != fa->add) {
        apply_add(fa->add - pas);
        pas = fa->add;
      }
      if (rev) {
        v[0]->rev ^= v[0] != nil;
        v[1]->rev ^= v[1] != nil;
        node *tmp = v[0];
        v[0] = v[1];
        v[1] = tmp;
        rev = false;
      }
    }

    void pull(node *nil) {
      if (v[0] != nil) {
        v[0]->push(nil);
      }
      if (v[1] != nil) {
        v[1]->push(nil);
      }
      siz = 1 + v[0]->siz + v[1]->siz + vsz;
      sum = val + v[0]->sum + v[1]->sum + vs;
    }

    bool is_auxiliary_root(node *nil) const {
      return fa == nil || (fa->v[0] != this && fa->v[1] != this);
    }

    void rotate(int dir, node *nil) {
      node *nr = v[dir ^ 1];
      node *mid = nr->v[dir];
      if (mid != nil) {
        mid->push(nil);
      }
      v[dir ^ 1] = mid;
      if (mid != nil) {
        mid->fa = this;
        mid->pas = add;
      }
      nr->v[dir] = this;
      nr->fa = fa;
      nr->pas = fa->add;
      if (fa->v[0] == this) {
        fa->v[0] = nr;
      }
      if (fa->v[1] == this) {
        fa->v[1] = nr;
      }
      fa = nr;
      pas = nr->add;
      pull(nil);
      nr->pull(nil);
    }

    void splay(node *nil) {
      while (!is_auxiliary_root(nil)) {
        if (fa->is_auxiliary_root(nil)) {
          fa->push(nil);
          push(nil);
          fa->rotate(fa->v[0] == this, nil);
        } else {
          node *gf = fa->fa;
          gf->push(nil);
          fa->push(nil);
          push(nil);
          bool pil = gf->v[0] == fa;
          bool vil = fa->v[0] == this;
          if (pil == vil) {
            gf->rotate(pil, nil);
          }
          fa->rotate(vil, nil);
          if (pil != vil) {
            gf->rotate(pil, nil);
          }
        }
      }
      push(nil);
    }

    void access(node *nil) {
      node *pre = nil;
      for (node *cur = this; cur != nil; cur = cur->fa) {
        cur->splay(nil);
        if (cur->v[1] != nil) {
          cur->v[1]->push(nil);
          cur->vsz += cur->v[1]->siz;
          cur->vs += cur->v[1]->sum;
        }
        cur->v[1] = pre;
        if (pre != nil) {
          pre->push(nil);
          cur->vsz -= pre->siz;
          cur->vs -= pre->sum;
        }
        // Moving a whole child between preferred and virtual sets preserves
        // cur's total size/sum. Their cached values need no recomputation.
        pre = cur;
      }
      splay(nil);
    }

    void make_root(node *nil) {
      access(nil);
      rev = !rev;
      push(nil);
    }

    void link_to(node *np, node *nil) {
      // make_root has already exposed this represented root.
      assert(fa == nil && v[0] == nil);
      np->access(nil);
      np->v[1] = this;
      fa = np;
      pas = np->add;
      np->pull(nil);
    }

    void cut_from_parent(node *nil) {
      access(nil);
      assert(v[0] != nil);
      v[0]->push(nil);
      v[0]->fa = nil;
      v[0] = nil;
      pull(nil);
    }
  };

  node ni1;
  std::vector<node> nd_;

  node *none() { return &ni1; }

  void check_vertex(int u) const { assert(0 <= u && u < int(nd_.size())); }

  node &expose_side(int u, int parent) {
    nd_[parent].make_root(none());
    node &cur = nd_[u];
    cur.access(none());
    assert(cur.v[0] != none() && cur.v[1] == none());
    // The final rotation's pull has synchronized the ancestor-side child.
    assert(cur.v[0]->pas == cur.add);
    return cur;
  }

public:
  explicit dynamic_tree_subtree(const std::vector<long long> &va1)
      : nd_(va1.size()) {
    assert(va1.size() <= std::size_t(std::numeric_limits<int>::max()));
    ni1.fa = &ni1;
    ni1.v[0] = ni1.v[1] = &ni1;
    for (int u = 0; u < int(nd_.size()); u++) {
      node &cur = nd_[u];
      cur.fa = none();
      cur.v[0] = cur.v[1] = none();
      cur.val = cur.sum = va1[u];
      cur.siz = 1;
    }
  }

  dynamic_tree_subtree(const dynamic_tree_subtree &) = delete;
  dynamic_tree_subtree &operator=(const dynamic_tree_subtree &) = delete;
  dynamic_tree_subtree(dynamic_tree_subtree &&) = delete;
  dynamic_tree_subtree &operator=(dynamic_tree_subtree &&) = delete;

  int size() const { return int(nd_.size()); }

  /// @brief Link two different represented trees with an undirected edge.
  void link(int a, int b) {
    check_vertex(a);
    check_vertex(b);
    nd_[a].make_root(none());
    nd_[a].link_to(&nd_[b], none());
  }

  /// @brief Remove an existing undirected edge.
  void cut(int a, int b) {
    check_vertex(a);
    check_vertex(b);
    nd_[a].make_root(none());
    nd_[b].cut_from_parent(none());
  }

  /// @brief Add dlt to vertex u.
  void vertex_add(int u, long long dlt) {
    check_vertex(u);
    if (dlt == 0)
      return;
    nd_[u].access(none());
    nd_[u].val += dlt;
    nd_[u].sum += dlt;
  }

  /// @brief Add dlt to the component on u's side after conceptually
  /// removing the adjacent edge (u,fa), then restore that edge.
  void subtree_add(int u, int fa, long long dlt) {
    check_vertex(u);
    check_vertex(fa);
    if (dlt == 0)
      return;
    node &cur = expose_side(u, fa);
    cur.val += dlt;
    cur.vs += static_cast<long long>(cur.vsz) * dlt;
    cur.sum += static_cast<long long>(1 + cur.vsz) * dlt;
    cur.add += dlt;
    // Only the vertex and its virtual descendants receive this increment.
    // Cancel it for the exposed ancestor side, without cutting the edge.
    cur.v[0]->pas = cur.add;
  }

  /// @brief Sum the component on u's side after conceptually removing
  /// the adjacent edge (u,fa), then restore that edge.
  long long subtree_sum(int u, int fa) {
    check_vertex(u);
    check_vertex(fa);
    node &cur = expose_side(u, fa);
    return cur.val + cur.vs;
  }
};

} // namespace noya

 // NOYA_DYNAMIC_TREE_SUBTREE_HPP
#ifndef NOYA_DYNAMIC_TREE_SUBTREE_HPP
#define NOYA_DYNAMIC_TREE_SUBTREE_HPP 1

/// @complexity Time: O(n) construction; amortized O(log(n+1)) per link,
/// cut, vertex update or directed-subtree operation; O(1) size. Space: O(n).

#include <cassert>
#include <cstddef>
#include <limits>
#include <vector>

namespace noya {

/// @brief Link-Cut Tree augmented with virtual-child size and sum. Access
/// moves preferred children into or out of the virtual aggregate; each node
/// remembers how much of its represented parent's lazy addition it has
/// already received, so subtree additions remain valid across preferred-path
/// changes. Vertices are [0,n), where n must fit int. Empty forests are valid.
/// link(a,b) requires different components; cut(a,b) requires an existing edge.
/// subtree_add/sum(u,p) require adjacent vertices and act on u's side of that
/// edge; p is not an arbitrary root. Values and deltas may be negative. All
/// sums, products and accumulated lazy-tag arithmetic must fit long long.
/// Nodes are owned by this object; copy/move are disabled to preserve pointers.
/// All tree operations and cleanup are iterative.
class dynamic_tree_subtree {
  struct node {
    node *fa = nullptr;
    node *v[2] = {nullptr, nullptr};
    long long val = 0;
    int siz = 0;
    int vsz = 0;
    long long sum = 0;
    long long vs = 0;
    long long add = 0;
    long long pas = 0;
    bool rev = false;

    void apply_add(long long dlt) {
      val += dlt;
      sum += static_cast<long long>(siz) * dlt;
      vs += static_cast<long long>(vsz) * dlt;
      add += dlt;
    }

    void push(node *nil) {
      // Most exposes carry no addition; avoid touching every aggregate then.
      if (fa != nil && pas != fa->add) {
        apply_add(fa->add - pas);
        pas = fa->add;
      }
      if (rev) {
        v[0]->rev ^= v[0] != nil;
        v[1]->rev ^= v[1] != nil;
        node *tmp = v[0];
        v[0] = v[1];
        v[1] = tmp;
        rev = false;
      }
    }

    void pull(node *nil) {
      if (v[0] != nil) {
        v[0]->push(nil);
      }
      if (v[1] != nil) {
        v[1]->push(nil);
      }
      siz = 1 + v[0]->siz + v[1]->siz + vsz;
      sum = val + v[0]->sum + v[1]->sum + vs;
    }

    bool is_auxiliary_root(node *nil) const {
      return fa == nil || (fa->v[0] != this && fa->v[1] != this);
    }

    void rotate(int dir, node *nil) {
      node *nr = v[dir ^ 1];
      node *mid = nr->v[dir];
      if (mid != nil) {
        mid->push(nil);
      }
      v[dir ^ 1] = mid;
      if (mid != nil) {
        mid->fa = this;
        mid->pas = add;
      }
      nr->v[dir] = this;
      nr->fa = fa;
      nr->pas = fa->add;
      if (fa->v[0] == this) {
        fa->v[0] = nr;
      }
      if (fa->v[1] == this) {
        fa->v[1] = nr;
      }
      fa = nr;
      pas = nr->add;
      pull(nil);
      nr->pull(nil);
    }

    void splay(node *nil) {
      while (!is_auxiliary_root(nil)) {
        if (fa->is_auxiliary_root(nil)) {
          fa->push(nil);
          push(nil);
          fa->rotate(fa->v[0] == this, nil);
        } else {
          node *gf = fa->fa;
          gf->push(nil);
          fa->push(nil);
          push(nil);
          bool pil = gf->v[0] == fa;
          bool vil = fa->v[0] == this;
          if (pil == vil) {
            gf->rotate(pil, nil);
          }
          fa->rotate(vil, nil);
          if (pil != vil) {
            gf->rotate(pil, nil);
          }
        }
      }
      push(nil);
    }

    void access(node *nil) {
      node *pre = nil;
      for (node *cur = this; cur != nil; cur = cur->fa) {
        cur->splay(nil);
        if (cur->v[1] != nil) {
          cur->v[1]->push(nil);
          cur->vsz += cur->v[1]->siz;
          cur->vs += cur->v[1]->sum;
        }
        cur->v[1] = pre;
        if (pre != nil) {
          pre->push(nil);
          cur->vsz -= pre->siz;
          cur->vs -= pre->sum;
        }
        // Moving a whole child between preferred and virtual sets preserves
        // cur's total size/sum. Their cached values need no recomputation.
        pre = cur;
      }
      splay(nil);
    }

    void make_root(node *nil) {
      access(nil);
      rev = !rev;
      push(nil);
    }

    void link_to(node *np, node *nil) {
      // make_root has already exposed this represented root.
      assert(fa == nil && v[0] == nil);
      np->access(nil);
      np->v[1] = this;
      fa = np;
      pas = np->add;
      np->pull(nil);
    }

    void cut_from_parent(node *nil) {
      access(nil);
      assert(v[0] != nil);
      v[0]->push(nil);
      v[0]->fa = nil;
      v[0] = nil;
      pull(nil);
    }
  };

  node ni1;
  std::vector<node> nd_;

  node *none() { return &ni1; }

  void check_vertex(int u) const { assert(0 <= u && u < int(nd_.size())); }

  node &expose_side(int u, int parent) {
    nd_[parent].make_root(none());
    node &cur = nd_[u];
    cur.access(none());
    assert(cur.v[0] != none() && cur.v[1] == none());
    // The final rotation's pull has synchronized the ancestor-side child.
    assert(cur.v[0]->pas == cur.add);
    return cur;
  }

public:
  explicit dynamic_tree_subtree(const std::vector<long long> &va1)
      : nd_(va1.size()) {
    assert(va1.size() <= std::size_t(std::numeric_limits<int>::max()));
    ni1.fa = &ni1;
    ni1.v[0] = ni1.v[1] = &ni1;
    for (int u = 0; u < int(nd_.size()); u++) {
      node &cur = nd_[u];
      cur.fa = none();
      cur.v[0] = cur.v[1] = none();
      cur.val = cur.sum = va1[u];
      cur.siz = 1;
    }
  }

  dynamic_tree_subtree(const dynamic_tree_subtree &) = delete;
  dynamic_tree_subtree &operator=(const dynamic_tree_subtree &) = delete;
  dynamic_tree_subtree(dynamic_tree_subtree &&) = delete;
  dynamic_tree_subtree &operator=(dynamic_tree_subtree &&) = delete;

  int size() const { return int(nd_.size()); }

  /// @brief Link two different represented trees with an undirected edge.
  void link(int a, int b) {
    check_vertex(a);
    check_vertex(b);
    nd_[a].make_root(none());
    nd_[a].link_to(&nd_[b], none());
  }

  /// @brief Remove an existing undirected edge.
  void cut(int a, int b) {
    check_vertex(a);
    check_vertex(b);
    nd_[a].make_root(none());
    nd_[b].cut_from_parent(none());
  }

  /// @brief Add dlt to vertex u.
  void vertex_add(int u, long long dlt) {
    check_vertex(u);
    if (dlt == 0)
      return;
    nd_[u].access(none());
    nd_[u].val += dlt;
    nd_[u].sum += dlt;
  }

  /// @brief Add dlt to the component on u's side after conceptually
  /// removing the adjacent edge (u,fa), then restore that edge.
  void subtree_add(int u, int fa, long long dlt) {
    check_vertex(u);
    check_vertex(fa);
    if (dlt == 0)
      return;
    node &cur = expose_side(u, fa);
    cur.val += dlt;
    cur.vs += static_cast<long long>(cur.vsz) * dlt;
    cur.sum += static_cast<long long>(1 + cur.vsz) * dlt;
    cur.add += dlt;
    // Only the vertex and its virtual descendants receive this increment.
    // Cancel it for the exposed ancestor side, without cutting the edge.
    cur.v[0]->pas = cur.add;
  }

  /// @brief Sum the component on u's side after conceptually removing
  /// the adjacent edge (u,fa), then restore that edge.
  long long subtree_sum(int u, int fa) {
    check_vertex(u);
    check_vertex(fa);
    node &cur = expose_side(u, fa);
    return cur.val + cur.vs;
  }
};

} // namespace noya

#endif // NOYA_DYNAMIC_TREE_SUBTREE_HPP
/// @complexity Time: O(n) construction; amortized O(log(n+1)) per link,
/// cut, vertex update or directed-subtree operation; O(1) size. Space: O(n).

#include <cassert>
#include <cstddef>
#include <limits>
#include <vector>

namespace noya {

/// @brief Link-Cut Tree augmented with virtual-child size and sum. Access
/// moves preferred children into or out of the virtual aggregate; each node
/// remembers how much of its represented parent's lazy addition it has
/// already received, so subtree additions remain valid across preferred-path
/// changes. Vertices are [0,n), where n must fit int. Empty forests are valid.
/// link(a,b) requires different components; cut(a,b) requires an existing edge.
/// subtree_add/sum(u,p) require adjacent vertices and act on u's side of that
/// edge; p is not an arbitrary root. Values and deltas may be negative. All
/// sums, products and accumulated lazy-tag arithmetic must fit long long.
/// Nodes are owned by this object; copy/move are disabled to preserve pointers.
/// All tree operations and cleanup are iterative.
class dynamic_tree_subtree {
  struct node {
    node *fa = nullptr;
    node *v[2] = {nullptr, nullptr};
    long long val = 0;
    int siz = 0;
    int vsz = 0;
    long long sum = 0;
    long long vs = 0;
    long long add = 0;
    long long pas = 0;
    bool rev = false;

    void apply_add(long long dlt) {
      val += dlt;
      sum += static_cast<long long>(siz) * dlt;
      vs += static_cast<long long>(vsz) * dlt;
      add += dlt;
    }

    void push(node *nil) {
      // Most exposes carry no addition; avoid touching every aggregate then.
      if (fa != nil && pas != fa->add) {
        apply_add(fa->add - pas);
        pas = fa->add;
      }
      if (rev) {
        v[0]->rev ^= v[0] != nil;
        v[1]->rev ^= v[1] != nil;
        node *tmp = v[0];
        v[0] = v[1];
        v[1] = tmp;
        rev = false;
      }
    }

    void pull(node *nil) {
      if (v[0] != nil) {
        v[0]->push(nil);
      }
      if (v[1] != nil) {
        v[1]->push(nil);
      }
      siz = 1 + v[0]->siz + v[1]->siz + vsz;
      sum = val + v[0]->sum + v[1]->sum + vs;
    }

    bool is_auxiliary_root(node *nil) const {
      return fa == nil || (fa->v[0] != this && fa->v[1] != this);
    }

    void rotate(int dir, node *nil) {
      node *nr = v[dir ^ 1];
      node *mid = nr->v[dir];
      if (mid != nil) {
        mid->push(nil);
      }
      v[dir ^ 1] = mid;
      if (mid != nil) {
        mid->fa = this;
        mid->pas = add;
      }
      nr->v[dir] = this;
      nr->fa = fa;
      nr->pas = fa->add;
      if (fa->v[0] == this) {
        fa->v[0] = nr;
      }
      if (fa->v[1] == this) {
        fa->v[1] = nr;
      }
      fa = nr;
      pas = nr->add;
      pull(nil);
      nr->pull(nil);
    }

    void splay(node *nil) {
      while (!is_auxiliary_root(nil)) {
        if (fa->is_auxiliary_root(nil)) {
          fa->push(nil);
          push(nil);
          fa->rotate(fa->v[0] == this, nil);
        } else {
          node *gf = fa->fa;
          gf->push(nil);
          fa->push(nil);
          push(nil);
          bool pil = gf->v[0] == fa;
          bool vil = fa->v[0] == this;
          if (pil == vil) {
            gf->rotate(pil, nil);
          }
          fa->rotate(vil, nil);
          if (pil != vil) {
            gf->rotate(pil, nil);
          }
        }
      }
      push(nil);
    }

    void access(node *nil) {
      node *pre = nil;
      for (node *cur = this; cur != nil; cur = cur->fa) {
        cur->splay(nil);
        if (cur->v[1] != nil) {
          cur->v[1]->push(nil);
          cur->vsz += cur->v[1]->siz;
          cur->vs += cur->v[1]->sum;
        }
        cur->v[1] = pre;
        if (pre != nil) {
          pre->push(nil);
          cur->vsz -= pre->siz;
          cur->vs -= pre->sum;
        }
        // Moving a whole child between preferred and virtual sets preserves
        // cur's total size/sum. Their cached values need no recomputation.
        pre = cur;
      }
      splay(nil);
    }

    void make_root(node *nil) {
      access(nil);
      rev = !rev;
      push(nil);
    }

    void link_to(node *np, node *nil) {
      // make_root has already exposed this represented root.
      assert(fa == nil && v[0] == nil);
      np->access(nil);
      np->v[1] = this;
      fa = np;
      pas = np->add;
      np->pull(nil);
    }

    void cut_from_parent(node *nil) {
      access(nil);
      assert(v[0] != nil);
      v[0]->push(nil);
      v[0]->fa = nil;
      v[0] = nil;
      pull(nil);
    }
  };

  node ni1;
  std::vector<node> nd_;

  node *none() { return &ni1; }

  void check_vertex(int u) const { assert(0 <= u && u < int(nd_.size())); }

  node &expose_side(int u, int parent) {
    nd_[parent].make_root(none());
    node &cur = nd_[u];
    cur.access(none());
    assert(cur.v[0] != none() && cur.v[1] == none());
    // The final rotation's pull has synchronized the ancestor-side child.
    assert(cur.v[0]->pas == cur.add);
    return cur;
  }

public:
  explicit dynamic_tree_subtree(const std::vector<long long> &va1)
      : nd_(va1.size()) {
    assert(va1.size() <= std::size_t(std::numeric_limits<int>::max()));
    ni1.fa = &ni1;
    ni1.v[0] = ni1.v[1] = &ni1;
    for (int u = 0; u < int(nd_.size()); u++) {
      node &cur = nd_[u];
      cur.fa = none();
      cur.v[0] = cur.v[1] = none();
      cur.val = cur.sum = va1[u];
      cur.siz = 1;
    }
  }

  dynamic_tree_subtree(const dynamic_tree_subtree &) = delete;
  dynamic_tree_subtree &operator=(const dynamic_tree_subtree &) = delete;
  dynamic_tree_subtree(dynamic_tree_subtree &&) = delete;
  dynamic_tree_subtree &operator=(dynamic_tree_subtree &&) = delete;

  int size() const { return int(nd_.size()); }

  /// @brief Link two different represented trees with an undirected edge.
  void link(int a, int b) {
    check_vertex(a);
    check_vertex(b);
    nd_[a].make_root(none());
    nd_[a].link_to(&nd_[b], none());
  }

  /// @brief Remove an existing undirected edge.
  void cut(int a, int b) {
    check_vertex(a);
    check_vertex(b);
    nd_[a].make_root(none());
    nd_[b].cut_from_parent(none());
  }

  /// @brief Add dlt to vertex u.
  void vertex_add(int u, long long dlt) {
    check_vertex(u);
    if (dlt == 0)
      return;
    nd_[u].access(none());
    nd_[u].val += dlt;
    nd_[u].sum += dlt;
  }

  /// @brief Add dlt to the component on u's side after conceptually
  /// removing the adjacent edge (u,fa), then restore that edge.
  void subtree_add(int u, int fa, long long dlt) {
    check_vertex(u);
    check_vertex(fa);
    if (dlt == 0)
      return;
    node &cur = expose_side(u, fa);
    cur.val += dlt;
    cur.vs += static_cast<long long>(cur.vsz) * dlt;
    cur.sum += static_cast<long long>(1 + cur.vsz) * dlt;
    cur.add += dlt;
    // Only the vertex and its virtual descendants receive this increment.
    // Cancel it for the exposed ancestor side, without cutting the edge.
    cur.v[0]->pas = cur.add;
  }

  /// @brief Sum the component on u's side after conceptually removing
  /// the adjacent edge (u,fa), then restore that edge.
  long long subtree_sum(int u, int fa) {
    check_vertex(u);
    check_vertex(fa);
    node &cur = expose_side(u, fa);
    return cur.val + cur.vs;
  }
};

} // namespace noya

 // NOYA_DYNAMIC_TREE_SUBTREE_HPP