Skip to content

tree_contour.hpp

SECTIONGraph INCLUDEnoya/graph/tree_contour.hpp

静态树上维护点加/距离区间和、距离区间加/单点取值两套独立状态;点分治复用子树大小,按距离累加后线性建 Fenwick,构造 \(O(n\log n)\)。

Complexity: Time: \(O(n \log n)\) construction and \(O(\log^{2} n)\) per update or query. Space: \(O(n \log n)\); \(O(n)\) recursive depth during construction.

AC 记录:vertex_add_range_contour_sum_on_tree, vertex_get_range_contour_add_on_tree。

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n log n) construction and O(log^2 n) per update or
/// query. Space: O(n log n); O(n) recursive depth during construction.

#include <algorithm>
#include <cassert>
#include <limits>
#include <vector>

namespace noya {

/// @brief Maintain values indexed by tree distance. Every vertex is recorded
/// in all centroid-ancestor buckets; querying adds all buckets and subtracts
/// the bucket of the branch containing the query vertex. Fenwick trees over
/// distances support two independent channels: point-add/range-sum and
/// range-add/point-get. Both start from the supplied initial values; updates in
/// one channel do not affect the other. Intervals are half-open [l,r), including
/// arbitrary int endpoints (negative distances simply contain no vertices).
/// Input must be an undirected tree with valid endpoints, or empty; it is not
/// retained or modified. Initial values are empty (all zero) or have size n.
/// Vertex count is at most INT_MAX-2. Values, negated updates, bucket sums and
/// arithmetic intermediates must fit long long. Construction uses 512 MiB
/// stacks in local tests. Copies own independent state.
class tree_contour {
  struct fenwick {
    std::vector<long long> dat;

    fenwick() = default;
    explicit fenwick(int siz) : dat(siz + 1) {}

    int size() const { return int(dat.size()) - 1; }

    // Convert the per-distance entries to a Fenwick tree in linear time.
    void build() {
      for (std::size_t i = 1; i < dat.size(); ++i) {
        std::size_t parent = i + (i & -i);
        if (parent < dat.size()) dat[parent] += dat[i];
      }
    }

    void add(int i, long long val) {
      assert(0 <= i && i < size());
      for (std::size_t k = std::size_t(i) + 1; k < dat.size(); k += k & -k) {
        dat[k] += val;
      }
    }

    long long prefix_sum(int cnt) const {
      cnt = std::clamp(cnt, 0, size());
      long long res = 0;
      for (; cnt > 0; cnt -= cnt & -cnt) {
        res += dat[cnt];
      }
      return res;
    }
  };

  struct bucket {
    int nd = 0;
    fenwick bit;
    fenwick tag;

    void reset(int cnt) {
      nd = cnt;
      bit = fenwick(cnt);
      tag = fenwick(cnt + 1);
    }

    void point_add(int dis, long long val) { bit.add(dis, val); }

    long long point_prefix(int lim) const { return bit.prefix_sum(lim); }

    void range_add(int l, int r, long long val) {
      l = std::clamp(l, 0, nd);
      r = std::clamp(r, 0, nd);
      if (l >= r) {
        return;
      }
      tag.add(l, val);
      tag.add(r, -val);
    }

    long long point_tag(int dis) const {
      assert(0 <= dis && dis < nd);
      return tag.prefix_sum(dis + 1);
    }
  };

  struct ancestor_entry {
    int cen = -1;
    int dis = 0;
    int br = -1;
  };

  int n_ = 0;
  std::vector<std::vector<ancestor_entry>> up_;
  std::vector<bucket> a_;
  std::vector<std::vector<bucket>> br_;
  std::vector<long long> iv;

public:
  explicit tree_contour(const std::vector<std::vector<int>> &g,
                        const std::vector<long long> &iv1 = {})
      : n_(int(g.size())), up_(n_), a_(n_), br_(n_),
        iv(iv1.empty() ? std::vector<long long>(n_) : iv1) {
    assert(g.size() <= std::size_t(std::numeric_limits<int>::max() - 2));
    assert(iv.size() == g.size());
    if (n_ == 0) {
      return;
    }
    std::vector<int> size(n_, 1);
    std::vector<char> removed(n_);
    auto init = [&](auto &self, int u, int parent) -> void {
      for (int v : g[u]) if (v != parent) {
        assert(0 <= v && v < n_);
        self(self, v, u);
        size[u] += size[v];
      }
    };
    init(init, 0, -1);
    auto decompose = [&](auto &self, int entry) -> void {
      int total = size[entry], cen = entry;
      for (;;) {
        int next = -1;
        for (int v : g[cen]) if (!removed[v] && size[v] > total / 2) {
          next = v;
          break;
        }
        if (next == -1) break;
        // Reroot sizes across this edge; they remain valid for child components.
        size[cen] -= size[next];
        size[next] += size[cen];
        cen = next;
      }
      removed[cen] = true;
      up_[cen].push_back({cen, 0, -1});
      int maximum = 0;
      for (int v : g[cen]) if (!removed[v]) {
        int branch = int(br_[cen].size());
        auto collect = [&](auto &walk, int u, int parent, int distance) -> int {
          up_[u].push_back({cen, distance, branch});
          int farthest = distance;
          for (int w : g[u]) if (!removed[w] && w != parent)
            farthest = std::max(farthest, walk(walk, w, u, distance + 1));
          return farthest;
        };
        int farthest = collect(collect, v, cen, 1);
        br_[cen].push_back({});
        br_[cen].back().reset(farthest + 1);
        maximum = std::max(maximum, farthest);
      }
      a_[cen].reset(maximum + 1);
      for (int v : g[cen]) if (!removed[v]) self(self, v);
    };
    decompose(decompose, 0);
    if (!iv1.empty()) {
      for (int u = 0; u < n_; ++u) {
        for (const ancestor_entry &ent : up_[u]) {
          a_[ent.cen].bit.dat[ent.dis + 1] += iv[u];
          if (ent.br != -1)
            br_[ent.cen][ent.br].bit.dat[ent.dis + 1] += iv[u];
        }
      }
      for (int cen = 0; cen < n_; ++cen) {
        a_[cen].bit.build();
        for (auto &branch : br_[cen]) branch.bit.build();
      }
    }
  }

  /// @brief Add value to one vertex for later contour-sum queries.
  void point_add(int u, long long val) {
    assert(0 <= u && u < n_);
    for (const ancestor_entry &ent : up_[u]) {
      a_[ent.cen].point_add(ent.dis, val);
      if (ent.br != -1) {
        br_[ent.cen][ent.br].point_add(ent.dis, val);
      }
    }
  }

  /// @brief Sum values at vertices whose distance from vertex is in [l,r).
  long long range_sum(int u, int l, int r) const {
    assert(0 <= u && u < n_ && l <= r);
    l = std::clamp(l, 0, n_);
    r = std::clamp(r, 0, n_);
    if (l == r) return 0;
    long long res = 0;
    for (const ancestor_entry &ent : up_[u]) {
      int hi = r - ent.dis;
      if (hi <= 0) continue;
      int lo = l - ent.dis;
      res += a_[ent.cen].point_prefix(hi) - a_[ent.cen].point_prefix(lo);
      if (ent.br != -1)
        res -= br_[ent.cen][ent.br].point_prefix(hi)
             - br_[ent.cen][ent.br].point_prefix(lo);
    }
    return res;
  }

  /// @brief Add value to vertices whose distance from vertex is in [l,r).
  void range_add(int u, int l, int r, long long val) {
    assert(0 <= u && u < n_ && l <= r);
    l = std::clamp(l, 0, n_);
    r = std::clamp(r, 0, n_);
    if (l == r) return;
    for (const ancestor_entry &ent : up_[u]) {
      int ql = l - ent.dis;
      int qr = r - ent.dis;
      a_[ent.cen].range_add(ql, qr, val);
      if (ent.br != -1) {
        br_[ent.cen][ent.br].range_add(ql, qr, val);
      }
    }
  }

  /// @brief Return the initial value plus every contour-range addition that
  /// contains this vertex.
  long long point_get(int u) const {
    assert(0 <= u && u < n_);
    long long res = iv[u];
    for (const ancestor_entry &ent : up_[u]) {
      res += a_[ent.cen].point_tag(ent.dis);
      if (ent.br != -1) {
        res -= br_[ent.cen][ent.br].point_tag(ent.dis);
      }
    }
    return res;
  }
};

} // namespace noya

 // NOYA_TREE_CONTOUR_HPP
#ifndef NOYA_TREE_CONTOUR_HPP
#define NOYA_TREE_CONTOUR_HPP 1

/// @complexity Time: O(n log n) construction and O(log^2 n) per update or
/// query. Space: O(n log n); O(n) recursive depth during construction.

#include <algorithm>
#include <cassert>
#include <limits>
#include <vector>

namespace noya {

/// @brief Maintain values indexed by tree distance. Every vertex is recorded
/// in all centroid-ancestor buckets; querying adds all buckets and subtracts
/// the bucket of the branch containing the query vertex. Fenwick trees over
/// distances support two independent channels: point-add/range-sum and
/// range-add/point-get. Both start from the supplied initial values; updates in
/// one channel do not affect the other. Intervals are half-open [l,r), including
/// arbitrary int endpoints (negative distances simply contain no vertices).
/// Input must be an undirected tree with valid endpoints, or empty; it is not
/// retained or modified. Initial values are empty (all zero) or have size n.
/// Vertex count is at most INT_MAX-2. Values, negated updates, bucket sums and
/// arithmetic intermediates must fit long long. Construction uses 512 MiB
/// stacks in local tests. Copies own independent state.
class tree_contour {
  struct fenwick {
    std::vector<long long> dat;

    fenwick() = default;
    explicit fenwick(int siz) : dat(siz + 1) {}

    int size() const { return int(dat.size()) - 1; }

    // Convert the per-distance entries to a Fenwick tree in linear time.
    void build() {
      for (std::size_t i = 1; i < dat.size(); ++i) {
        std::size_t parent = i + (i & -i);
        if (parent < dat.size()) dat[parent] += dat[i];
      }
    }

    void add(int i, long long val) {
      assert(0 <= i && i < size());
      for (std::size_t k = std::size_t(i) + 1; k < dat.size(); k += k & -k) {
        dat[k] += val;
      }
    }

    long long prefix_sum(int cnt) const {
      cnt = std::clamp(cnt, 0, size());
      long long res = 0;
      for (; cnt > 0; cnt -= cnt & -cnt) {
        res += dat[cnt];
      }
      return res;
    }
  };

  struct bucket {
    int nd = 0;
    fenwick bit;
    fenwick tag;

    void reset(int cnt) {
      nd = cnt;
      bit = fenwick(cnt);
      tag = fenwick(cnt + 1);
    }

    void point_add(int dis, long long val) { bit.add(dis, val); }

    long long point_prefix(int lim) const { return bit.prefix_sum(lim); }

    void range_add(int l, int r, long long val) {
      l = std::clamp(l, 0, nd);
      r = std::clamp(r, 0, nd);
      if (l >= r) {
        return;
      }
      tag.add(l, val);
      tag.add(r, -val);
    }

    long long point_tag(int dis) const {
      assert(0 <= dis && dis < nd);
      return tag.prefix_sum(dis + 1);
    }
  };

  struct ancestor_entry {
    int cen = -1;
    int dis = 0;
    int br = -1;
  };

  int n_ = 0;
  std::vector<std::vector<ancestor_entry>> up_;
  std::vector<bucket> a_;
  std::vector<std::vector<bucket>> br_;
  std::vector<long long> iv;

public:
  explicit tree_contour(const std::vector<std::vector<int>> &g,
                        const std::vector<long long> &iv1 = {})
      : n_(int(g.size())), up_(n_), a_(n_), br_(n_),
        iv(iv1.empty() ? std::vector<long long>(n_) : iv1) {
    assert(g.size() <= std::size_t(std::numeric_limits<int>::max() - 2));
    assert(iv.size() == g.size());
    if (n_ == 0) {
      return;
    }
    std::vector<int> size(n_, 1);
    std::vector<char> removed(n_);
    auto init = [&](auto &self, int u, int parent) -> void {
      for (int v : g[u]) if (v != parent) {
        assert(0 <= v && v < n_);
        self(self, v, u);
        size[u] += size[v];
      }
    };
    init(init, 0, -1);
    auto decompose = [&](auto &self, int entry) -> void {
      int total = size[entry], cen = entry;
      for (;;) {
        int next = -1;
        for (int v : g[cen]) if (!removed[v] && size[v] > total / 2) {
          next = v;
          break;
        }
        if (next == -1) break;
        // Reroot sizes across this edge; they remain valid for child components.
        size[cen] -= size[next];
        size[next] += size[cen];
        cen = next;
      }
      removed[cen] = true;
      up_[cen].push_back({cen, 0, -1});
      int maximum = 0;
      for (int v : g[cen]) if (!removed[v]) {
        int branch = int(br_[cen].size());
        auto collect = [&](auto &walk, int u, int parent, int distance) -> int {
          up_[u].push_back({cen, distance, branch});
          int farthest = distance;
          for (int w : g[u]) if (!removed[w] && w != parent)
            farthest = std::max(farthest, walk(walk, w, u, distance + 1));
          return farthest;
        };
        int farthest = collect(collect, v, cen, 1);
        br_[cen].push_back({});
        br_[cen].back().reset(farthest + 1);
        maximum = std::max(maximum, farthest);
      }
      a_[cen].reset(maximum + 1);
      for (int v : g[cen]) if (!removed[v]) self(self, v);
    };
    decompose(decompose, 0);
    if (!iv1.empty()) {
      for (int u = 0; u < n_; ++u) {
        for (const ancestor_entry &ent : up_[u]) {
          a_[ent.cen].bit.dat[ent.dis + 1] += iv[u];
          if (ent.br != -1)
            br_[ent.cen][ent.br].bit.dat[ent.dis + 1] += iv[u];
        }
      }
      for (int cen = 0; cen < n_; ++cen) {
        a_[cen].bit.build();
        for (auto &branch : br_[cen]) branch.bit.build();
      }
    }
  }

  /// @brief Add value to one vertex for later contour-sum queries.
  void point_add(int u, long long val) {
    assert(0 <= u && u < n_);
    for (const ancestor_entry &ent : up_[u]) {
      a_[ent.cen].point_add(ent.dis, val);
      if (ent.br != -1) {
        br_[ent.cen][ent.br].point_add(ent.dis, val);
      }
    }
  }

  /// @brief Sum values at vertices whose distance from vertex is in [l,r).
  long long range_sum(int u, int l, int r) const {
    assert(0 <= u && u < n_ && l <= r);
    l = std::clamp(l, 0, n_);
    r = std::clamp(r, 0, n_);
    if (l == r) return 0;
    long long res = 0;
    for (const ancestor_entry &ent : up_[u]) {
      int hi = r - ent.dis;
      if (hi <= 0) continue;
      int lo = l - ent.dis;
      res += a_[ent.cen].point_prefix(hi) - a_[ent.cen].point_prefix(lo);
      if (ent.br != -1)
        res -= br_[ent.cen][ent.br].point_prefix(hi)
             - br_[ent.cen][ent.br].point_prefix(lo);
    }
    return res;
  }

  /// @brief Add value to vertices whose distance from vertex is in [l,r).
  void range_add(int u, int l, int r, long long val) {
    assert(0 <= u && u < n_ && l <= r);
    l = std::clamp(l, 0, n_);
    r = std::clamp(r, 0, n_);
    if (l == r) return;
    for (const ancestor_entry &ent : up_[u]) {
      int ql = l - ent.dis;
      int qr = r - ent.dis;
      a_[ent.cen].range_add(ql, qr, val);
      if (ent.br != -1) {
        br_[ent.cen][ent.br].range_add(ql, qr, val);
      }
    }
  }

  /// @brief Return the initial value plus every contour-range addition that
  /// contains this vertex.
  long long point_get(int u) const {
    assert(0 <= u && u < n_);
    long long res = iv[u];
    for (const ancestor_entry &ent : up_[u]) {
      res += a_[ent.cen].point_tag(ent.dis);
      if (ent.br != -1) {
        res -= br_[ent.cen][ent.br].point_tag(ent.dis);
      }
    }
    return res;
  }
};

} // namespace noya

#endif // NOYA_TREE_CONTOUR_HPP
/// @complexity Time: O(n log n) construction and O(log^2 n) per update or
/// query. Space: O(n log n); O(n) recursive depth during construction.

#include <algorithm>
#include <cassert>
#include <limits>
#include <vector>

namespace noya {

/// @brief Maintain values indexed by tree distance. Every vertex is recorded
/// in all centroid-ancestor buckets; querying adds all buckets and subtracts
/// the bucket of the branch containing the query vertex. Fenwick trees over
/// distances support two independent channels: point-add/range-sum and
/// range-add/point-get. Both start from the supplied initial values; updates in
/// one channel do not affect the other. Intervals are half-open [l,r), including
/// arbitrary int endpoints (negative distances simply contain no vertices).
/// Input must be an undirected tree with valid endpoints, or empty; it is not
/// retained or modified. Initial values are empty (all zero) or have size n.
/// Vertex count is at most INT_MAX-2. Values, negated updates, bucket sums and
/// arithmetic intermediates must fit long long. Construction uses 512 MiB
/// stacks in local tests. Copies own independent state.
class tree_contour {
  struct fenwick {
    std::vector<long long> dat;

    fenwick() = default;
    explicit fenwick(int siz) : dat(siz + 1) {}

    int size() const { return int(dat.size()) - 1; }

    // Convert the per-distance entries to a Fenwick tree in linear time.
    void build() {
      for (std::size_t i = 1; i < dat.size(); ++i) {
        std::size_t parent = i + (i & -i);
        if (parent < dat.size()) dat[parent] += dat[i];
      }
    }

    void add(int i, long long val) {
      assert(0 <= i && i < size());
      for (std::size_t k = std::size_t(i) + 1; k < dat.size(); k += k & -k) {
        dat[k] += val;
      }
    }

    long long prefix_sum(int cnt) const {
      cnt = std::clamp(cnt, 0, size());
      long long res = 0;
      for (; cnt > 0; cnt -= cnt & -cnt) {
        res += dat[cnt];
      }
      return res;
    }
  };

  struct bucket {
    int nd = 0;
    fenwick bit;
    fenwick tag;

    void reset(int cnt) {
      nd = cnt;
      bit = fenwick(cnt);
      tag = fenwick(cnt + 1);
    }

    void point_add(int dis, long long val) { bit.add(dis, val); }

    long long point_prefix(int lim) const { return bit.prefix_sum(lim); }

    void range_add(int l, int r, long long val) {
      l = std::clamp(l, 0, nd);
      r = std::clamp(r, 0, nd);
      if (l >= r) {
        return;
      }
      tag.add(l, val);
      tag.add(r, -val);
    }

    long long point_tag(int dis) const {
      assert(0 <= dis && dis < nd);
      return tag.prefix_sum(dis + 1);
    }
  };

  struct ancestor_entry {
    int cen = -1;
    int dis = 0;
    int br = -1;
  };

  int n_ = 0;
  std::vector<std::vector<ancestor_entry>> up_;
  std::vector<bucket> a_;
  std::vector<std::vector<bucket>> br_;
  std::vector<long long> iv;

public:
  explicit tree_contour(const std::vector<std::vector<int>> &g,
                        const std::vector<long long> &iv1 = {})
      : n_(int(g.size())), up_(n_), a_(n_), br_(n_),
        iv(iv1.empty() ? std::vector<long long>(n_) : iv1) {
    assert(g.size() <= std::size_t(std::numeric_limits<int>::max() - 2));
    assert(iv.size() == g.size());
    if (n_ == 0) {
      return;
    }
    std::vector<int> size(n_, 1);
    std::vector<char> removed(n_);
    auto init = [&](auto &self, int u, int parent) -> void {
      for (int v : g[u]) if (v != parent) {
        assert(0 <= v && v < n_);
        self(self, v, u);
        size[u] += size[v];
      }
    };
    init(init, 0, -1);
    auto decompose = [&](auto &self, int entry) -> void {
      int total = size[entry], cen = entry;
      for (;;) {
        int next = -1;
        for (int v : g[cen]) if (!removed[v] && size[v] > total / 2) {
          next = v;
          break;
        }
        if (next == -1) break;
        // Reroot sizes across this edge; they remain valid for child components.
        size[cen] -= size[next];
        size[next] += size[cen];
        cen = next;
      }
      removed[cen] = true;
      up_[cen].push_back({cen, 0, -1});
      int maximum = 0;
      for (int v : g[cen]) if (!removed[v]) {
        int branch = int(br_[cen].size());
        auto collect = [&](auto &walk, int u, int parent, int distance) -> int {
          up_[u].push_back({cen, distance, branch});
          int farthest = distance;
          for (int w : g[u]) if (!removed[w] && w != parent)
            farthest = std::max(farthest, walk(walk, w, u, distance + 1));
          return farthest;
        };
        int farthest = collect(collect, v, cen, 1);
        br_[cen].push_back({});
        br_[cen].back().reset(farthest + 1);
        maximum = std::max(maximum, farthest);
      }
      a_[cen].reset(maximum + 1);
      for (int v : g[cen]) if (!removed[v]) self(self, v);
    };
    decompose(decompose, 0);
    if (!iv1.empty()) {
      for (int u = 0; u < n_; ++u) {
        for (const ancestor_entry &ent : up_[u]) {
          a_[ent.cen].bit.dat[ent.dis + 1] += iv[u];
          if (ent.br != -1)
            br_[ent.cen][ent.br].bit.dat[ent.dis + 1] += iv[u];
        }
      }
      for (int cen = 0; cen < n_; ++cen) {
        a_[cen].bit.build();
        for (auto &branch : br_[cen]) branch.bit.build();
      }
    }
  }

  /// @brief Add value to one vertex for later contour-sum queries.
  void point_add(int u, long long val) {
    assert(0 <= u && u < n_);
    for (const ancestor_entry &ent : up_[u]) {
      a_[ent.cen].point_add(ent.dis, val);
      if (ent.br != -1) {
        br_[ent.cen][ent.br].point_add(ent.dis, val);
      }
    }
  }

  /// @brief Sum values at vertices whose distance from vertex is in [l,r).
  long long range_sum(int u, int l, int r) const {
    assert(0 <= u && u < n_ && l <= r);
    l = std::clamp(l, 0, n_);
    r = std::clamp(r, 0, n_);
    if (l == r) return 0;
    long long res = 0;
    for (const ancestor_entry &ent : up_[u]) {
      int hi = r - ent.dis;
      if (hi <= 0) continue;
      int lo = l - ent.dis;
      res += a_[ent.cen].point_prefix(hi) - a_[ent.cen].point_prefix(lo);
      if (ent.br != -1)
        res -= br_[ent.cen][ent.br].point_prefix(hi)
             - br_[ent.cen][ent.br].point_prefix(lo);
    }
    return res;
  }

  /// @brief Add value to vertices whose distance from vertex is in [l,r).
  void range_add(int u, int l, int r, long long val) {
    assert(0 <= u && u < n_ && l <= r);
    l = std::clamp(l, 0, n_);
    r = std::clamp(r, 0, n_);
    if (l == r) return;
    for (const ancestor_entry &ent : up_[u]) {
      int ql = l - ent.dis;
      int qr = r - ent.dis;
      a_[ent.cen].range_add(ql, qr, val);
      if (ent.br != -1) {
        br_[ent.cen][ent.br].range_add(ql, qr, val);
      }
    }
  }

  /// @brief Return the initial value plus every contour-range addition that
  /// contains this vertex.
  long long point_get(int u) const {
    assert(0 <= u && u < n_);
    long long res = iv[u];
    for (const ancestor_entry &ent : up_[u]) {
      res += a_[ent.cen].point_tag(ent.dis);
      if (ent.br != -1) {
        res -= br_[ent.cen][ent.br].point_tag(ent.dis);
      }
    }
    return res;
  }
};

} // namespace noya

 // NOYA_TREE_CONTOUR_HPP