Skip to content

range_parallel_dsu.hpp

SECTIONData Structure INCLUDEnoya/range_parallel_dsu.hpp

批量合并两个等长区间中对应位置的点;适合大量区间等价约束或字符串片段对应关系。

Complexity: Time: O((n + q) log n alpha(n)) total for q range unions. Space: O(n log n).

AC 记录:range_parallel_unionfind

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O((n + q) log n alpha(n)) total for q range unions.
/// Space: O(n log n).

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

namespace noya {

/// @brief Add all equal-offset edges between two ranges and maintain a
/// component-pair aggregate. Level h represents intervals of length 2^h.
/// Merging two level-h classes recursively merges their left and right halves;
/// since each class pair succeeds only once, all downward propagation is
/// amortized over O(n log n) interval nodes. Any length is covered by its first
/// and last largest-power-of-two blocks.
template <class T> class range_parallel_dsu {
public:
  range_parallel_dsu() = default;

  explicit range_parallel_dsu(const std::vector<T> &w) {
    build(w);
  }

  void build(const std::vector<T> &w) {
    n = int(w.size());
    int lg = 1;
    while ((1 << lg) <= std::max(1, n)) {
      lg++;
    }
    fa.resize(lg);
    for (int dep = 0; dep < lg; dep++) {
      int len = 1 << dep;
      int cnt = std::max(0, n - len + 1);
      fa[dep].assign(cnt, -1);
    }
    s = w;
    ans = T{};
  }

  /// @brief Add edges (a+i,b+i) for 0<=i<len and return the new
  /// sum of weight products over unordered pairs in the same component.
  T unite_ranges(int a, int b, int len) {
    assert(len >= 0);
    assert(0 <= a && a + len <= n);
    assert(0 <= b && b + len <= n);
    if (len == 0) {
      return ans;
    }
    int dep = 31 - __builtin_clz(unsigned(len));
    int blk = 1 << dep;
    unite_blocks(dep, a, b);
    unite_blocks(dep, a + len - blk, b + len - blk);
    return ans;
  }

  T pair_sum() const { return ans; }

  bool same(int a, int b) {
    assert(0 <= a && a < n && 0 <= b && b < n);
    return find(0, a) == find(0, b);
  }

private:
  int n = 0;
  std::vector<std::vector<int>> fa;
  std::vector<T> s;
  T ans{};

  int find(int dep, int val) {
    int rt = val;
    while (fa[dep][rt] >= 0) {
      rt = fa[dep][rt];
    }
    while (val != rt) {
      int nxt = fa[dep][val];
      fa[dep][val] = rt;
      val = nxt;
    }
    return rt;
  }

  bool join_roots(int dep, int a, int b) {
    a = find(dep, a);
    b = find(dep, b);
    if (a == b) {
      return false;
    }
    if (fa[dep][a] > fa[dep][b]) {
      std::swap(a, b);
    }
    fa[dep][a] += fa[dep][b];
    fa[dep][b] = a;
    if (dep == 0) {
      ans += s[a] * s[b];
      s[a] += s[b];
    }
    return true;
  }

  void unite_blocks(int dep, int a, int b) {
    if (!join_roots(dep, a, b) || dep == 0) {
      return;
    }
    int hf = 1 << (dep - 1);
    unite_blocks(dep - 1, a, b);
    unite_blocks(dep - 1, a + hf, b + hf);
  }
};

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

/// @complexity Time: O((n + q) log n alpha(n)) total for q range unions.
/// Space: O(n log n).

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

namespace noya {

/// @brief Add all equal-offset edges between two ranges and maintain a
/// component-pair aggregate. Level h represents intervals of length 2^h.
/// Merging two level-h classes recursively merges their left and right halves;
/// since each class pair succeeds only once, all downward propagation is
/// amortized over O(n log n) interval nodes. Any length is covered by its first
/// and last largest-power-of-two blocks.
template <class T> class range_parallel_dsu {
public:
  range_parallel_dsu() = default;

  explicit range_parallel_dsu(const std::vector<T> &w) {
    build(w);
  }

  void build(const std::vector<T> &w) {
    n = int(w.size());
    int lg = 1;
    while ((1 << lg) <= std::max(1, n)) {
      lg++;
    }
    fa.resize(lg);
    for (int dep = 0; dep < lg; dep++) {
      int len = 1 << dep;
      int cnt = std::max(0, n - len + 1);
      fa[dep].assign(cnt, -1);
    }
    s = w;
    ans = T{};
  }

  /// @brief Add edges (a+i,b+i) for 0<=i<len and return the new
  /// sum of weight products over unordered pairs in the same component.
  T unite_ranges(int a, int b, int len) {
    assert(len >= 0);
    assert(0 <= a && a + len <= n);
    assert(0 <= b && b + len <= n);
    if (len == 0) {
      return ans;
    }
    int dep = 31 - __builtin_clz(unsigned(len));
    int blk = 1 << dep;
    unite_blocks(dep, a, b);
    unite_blocks(dep, a + len - blk, b + len - blk);
    return ans;
  }

  T pair_sum() const { return ans; }

  bool same(int a, int b) {
    assert(0 <= a && a < n && 0 <= b && b < n);
    return find(0, a) == find(0, b);
  }

private:
  int n = 0;
  std::vector<std::vector<int>> fa;
  std::vector<T> s;
  T ans{};

  int find(int dep, int val) {
    int rt = val;
    while (fa[dep][rt] >= 0) {
      rt = fa[dep][rt];
    }
    while (val != rt) {
      int nxt = fa[dep][val];
      fa[dep][val] = rt;
      val = nxt;
    }
    return rt;
  }

  bool join_roots(int dep, int a, int b) {
    a = find(dep, a);
    b = find(dep, b);
    if (a == b) {
      return false;
    }
    if (fa[dep][a] > fa[dep][b]) {
      std::swap(a, b);
    }
    fa[dep][a] += fa[dep][b];
    fa[dep][b] = a;
    if (dep == 0) {
      ans += s[a] * s[b];
      s[a] += s[b];
    }
    return true;
  }

  void unite_blocks(int dep, int a, int b) {
    if (!join_roots(dep, a, b) || dep == 0) {
      return;
    }
    int hf = 1 << (dep - 1);
    unite_blocks(dep - 1, a, b);
    unite_blocks(dep - 1, a + hf, b + hf);
  }
};

} // namespace noya

#endif // NOYA_RANGE_PARALLEL_DSU_HPP
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <utility>
#include <vector>

/// @complexity Time: O((n + q) log n alpha(n)) total for q range unions.
/// Space: O(n log n).

namespace noya {

/// @brief Add all equal-offset edges between two ranges and maintain a
/// component-pair aggregate. Level h represents intervals of length 2^h.
/// Merging two level-h classes recursively merges their left and right halves;
/// since each class pair succeeds only once, all downward propagation is
/// amortized over O(n log n) interval nodes. Any length is covered by its first
/// and last largest-power-of-two blocks.
template <class T> class range_parallel_dsu {
public:
  range_parallel_dsu() = default;

  explicit range_parallel_dsu(const std::vector<T> &w) {
    build(w);
  }

  void build(const std::vector<T> &w) {
    n = int(w.size());
    int lg = 1;
    while ((1 << lg) <= std::max(1, n)) {
      lg++;
    }
    fa.resize(lg);
    for (int dep = 0; dep < lg; dep++) {
      int len = 1 << dep;
      int cnt = std::max(0, n - len + 1);
      fa[dep].assign(cnt, -1);
    }
    s = w;
    ans = T{};
  }

  /// @brief Add edges (a+i,b+i) for 0<=i<len and return the new
  /// sum of weight products over unordered pairs in the same component.
  T unite_ranges(int a, int b, int len) {
    assert(len >= 0);
    assert(0 <= a && a + len <= n);
    assert(0 <= b && b + len <= n);
    if (len == 0) {
      return ans;
    }
    int dep = 31 - __builtin_clz(unsigned(len));
    int blk = 1 << dep;
    unite_blocks(dep, a, b);
    unite_blocks(dep, a + len - blk, b + len - blk);
    return ans;
  }

  T pair_sum() const { return ans; }

  bool same(int a, int b) {
    assert(0 <= a && a < n && 0 <= b && b < n);
    return find(0, a) == find(0, b);
  }

private:
  int n = 0;
  std::vector<std::vector<int>> fa;
  std::vector<T> s;
  T ans{};

  int find(int dep, int val) {
    int rt = val;
    while (fa[dep][rt] >= 0) {
      rt = fa[dep][rt];
    }
    while (val != rt) {
      int nxt = fa[dep][val];
      fa[dep][val] = rt;
      val = nxt;
    }
    return rt;
  }

  bool join_roots(int dep, int a, int b) {
    a = find(dep, a);
    b = find(dep, b);
    if (a == b) {
      return false;
    }
    if (fa[dep][a] > fa[dep][b]) {
      std::swap(a, b);
    }
    fa[dep][a] += fa[dep][b];
    fa[dep][b] = a;
    if (dep == 0) {
      ans += s[a] * s[b];
      s[a] += s[b];
    }
    return true;
  }

  void unite_blocks(int dep, int a, int b) {
    if (!join_roots(dep, a, b) || dep == 0) {
      return;
    }
    int hf = 1 << (dep - 1);
    unite_blocks(dep - 1, a, b);
    unite_blocks(dep - 1, a + hf, b + hf);
  }
};

} // namespace noya