Luzhiled's Library

This documentation is automatically generated by competitive-verifier/competitive-verifier

View the Project on GitHub ei1333/library

:heavy_check_mark: test/unittest/union-finds.test.cpp

Depends on

Code

// competitive-verifier: STANDALONE

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

#include "../../structure/union-find/bipartite-graph.hpp"
#include "../../structure/union-find/partially-persistent-union-find.hpp"
#include "../../structure/union-find/persistent-union-find.hpp"
#include "../../structure/union-find/union-find-undo.hpp"
#include "../../structure/union-find/union-find.hpp"
#include "../../structure/union-find/weighted-union-find.hpp"

int main() {
  {
    UnionFind uf(6);
    assert(uf.unite(0, 1));
    assert(uf.unite(1, 2));
    assert(!uf.unite(0, 2));
    assert(uf.same(0, 2));
    assert(!uf.same(0, 3));
    assert(uf.size(1) == 3);
    uf.unite(3, 4);
    auto groups = uf.groups();
    for (auto& group : groups) std::sort(group.begin(), group.end());
    std::sort(groups.begin(), groups.end());
    assert((groups == std::vector<std::vector<int>>{{0, 1, 2}, {3, 4}, {5}}));
  }

  {
    WeightedUnionFind<long long> uf(4);
    assert(uf.unite(0, 1, 3));
    assert(uf.unite(1, 2, -5));
    assert(uf.diff(0, 2) == -2);
    assert(uf.diff(2, 0) == 2);
    assert(!uf.unite(0, 2, -2));
  }

  {
    UnionFindUndo uf(4);
    uf.unite(0, 1);
    uf.snapshot();
    uf.unite(1, 2);
    assert(uf.size(0) == 3);
    uf.rollback();
    assert(uf.size(0) == 2);
    assert(uf.size(2) == 1);
    uf.unite(2, 3);
    uf.undo();
    assert(uf.size(2) == 1);
  }

  {
    PartiallyPersistentUnionFind uf(5);
    uf.unite(1, 0, 1);
    uf.unite(3, 1, 2);
    uf.unite(5, 3, 4);
    assert(uf.size(0, 0) == 1);
    assert(uf.size(1, 0) == 2);
    assert(uf.size(2, 2) == 1);
    assert(uf.size(3, 0) == 3);
    assert(uf.find(4, 0) == uf.find(4, 2));
    assert(uf.find(4, 0) != uf.find(4, 3));
  }

  {
    PersistentUnionFind original(5);
    auto first = original;
    first.unite(0, 1);
    auto second = first;
    second.unite(1, 2);
    assert(original.size(0) == 1);
    assert(first.size(0) == 2);
    assert(first.size(2) == 1);
    assert(second.size(0) == 3);
  }

  {
    auto add_edge = [](BipartiteGraph& graph, int n, int u, int v) {
      graph.unite(u, v + n);
      graph.unite(u + n, v);
    };

    BipartiteGraph path(3);
    add_edge(path, 3, 0, 1);
    add_edge(path, 3, 1, 2);
    assert(path.bipartite_graph_coloring());
    assert(path[0] != path[1]);
    assert(path[1] != path[2]);

    BipartiteGraph triangle(3);
    add_edge(triangle, 3, 0, 1);
    add_edge(triangle, 3, 1, 2);
    add_edge(triangle, 3, 2, 0);
    assert(!triangle.bipartite_graph_coloring());
  }
}
#line 1 "test/unittest/union-finds.test.cpp"
// competitive-verifier: STANDALONE

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

#line 2 "structure/union-find/bipartite-graph.hpp"

#line 4 "structure/union-find/bipartite-graph.hpp"

#line 2 "structure/union-find/union-find.hpp"

#line 4 "structure/union-find/union-find.hpp"
#include <cstddef>
#include <utility>
#line 7 "structure/union-find/union-find.hpp"

struct UnionFind {
  std::vector<int> data;

  UnionFind() = default;

  explicit UnionFind(std::size_t sz) : data(sz, -1) {}

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if (x == y) return false;
    if (data[x] > data[y]) std::swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return true;
  }

  int find(int k) {
    if (data[k] < 0) return (k);
    return data[k] = find(data[k]);
  }

  int size(int k) { return -data[find(k)]; }

  bool same(int x, int y) { return find(x) == find(y); }

  std::vector<std::vector<int>> groups() {
    int n = (int)data.size();
    std::vector<std::vector<int>> ret(n);
    for (int i = 0; i < n; i++) {
      ret[find(i)].emplace_back(i);
    }
    ret.erase(
        std::remove_if(ret.begin(), ret.end(),
                       [&](const std::vector<int>& v) { return v.empty(); }),
        ret.end());
    return ret;
  }
};
#line 6 "structure/union-find/bipartite-graph.hpp"

struct BipartiteGraph : UnionFind {
  std::vector<int> color;

  BipartiteGraph(int v) : UnionFind(v + v), color(v + v, -1) {}

  bool bipartite_graph_coloring() {
    const int n = (int)color.size() / 2;
    for (int i = 0; i < n; i++) {
      int a = find(i);
      int b = find(i + n);
      if (a == b) return (false);
      if (color[a] < 0) color[a] = 0, color[b] = 1;
    }
    return (true);
  }

  bool operator[](int k) { return (bool(color[find(k)])); }
};
#line 2 "structure/union-find/partially-persistent-union-find.hpp"

#line 4 "structure/union-find/partially-persistent-union-find.hpp"
#include <iterator>
#line 7 "structure/union-find/partially-persistent-union-find.hpp"

struct PartiallyPersistentUnionFind {
  std::vector<int> data;
  std::vector<int> last;
  std::vector<std::vector<std::pair<int, int>>> add;

  PartiallyPersistentUnionFind() {}

  PartiallyPersistentUnionFind(int sz) : data(sz, -1), last(sz, 1e9), add(sz) {
    for (auto& vs : add) vs.emplace_back(-1, -1);
  }

  bool unite(int t, int x, int y) {
    x = find(t, x);
    y = find(t, y);
    if (x == y) return false;
    if (data[x] > data[y]) std::swap(x, y);
    data[x] += data[y];
    add[x].emplace_back(t, data[x]);
    data[y] = x;
    last[y] = t;
    return true;
  }

  int find(int t, int x) {
    if (t < last[x]) return x;
    return find(t, data[x]);
  }

  int size(int t, int x) {
    x = find(t, x);
    return -std::prev(std::lower_bound(add[x].begin(), add[x].end(),
                                       std::make_pair(t, 0)))
                ->second;
  }
};
#line 2 "structure/union-find/persistent-union-find.hpp"

#line 4 "structure/union-find/persistent-union-find.hpp"

#line 2 "structure/others/persistent-array.hpp"

#line 5 "structure/others/persistent-array.hpp"

template <typename T, int LOG>
struct PersistentArray {
  struct Node {
    T data;
    Node* child[1 << LOG] = {};

    Node() {}

    Node(const T& data) : data(data) {}
  };

  Node* root;

  PersistentArray() : root(nullptr) {}

  T get(Node* t, int k) {
    if (k == 0) return t->data;
    return get(t->child[k & ((1 << LOG) - 1)], k >> LOG);
  }

  T get(const int& k) { return get(root, k); }

  std::pair<Node*, T*> mutable_get(Node* t, int k) {
    t = t ? new Node(*t) : new Node();
    if (k == 0) return {t, &t->data};
    auto p = mutable_get(t->child[k & ((1 << LOG) - 1)], k >> LOG);
    t->child[k & ((1 << LOG) - 1)] = p.first;
    return {t, p.second};
  }

  T* mutable_get(const int& k) {
    auto ret = mutable_get(root, k);
    root = ret.first;
    return ret.second;
  }

  Node* build(Node* t, const T& data, int k) {
    if (!t) t = new Node();
    if (k == 0) {
      t->data = data;
      return t;
    }
    auto p = build(t->child[k & ((1 << LOG) - 1)], data, k >> LOG);
    t->child[k & ((1 << LOG) - 1)] = p;
    return t;
  }

  void build(const std::vector<T>& v) {
    root = nullptr;
    for (int i = 0; i < (int)v.size(); i++) {
      root = build(root, v[i], i);
    }
  }
};
#line 6 "structure/union-find/persistent-union-find.hpp"

/*
 * @brief Persistent-Union-Find(永続Union-Find)
 */
struct PersistentUnionFind {
  PersistentArray<int, 3> data;

  PersistentUnionFind() {}

  PersistentUnionFind(int sz) { data.build(std::vector<int>(sz, -1)); }

  int find(int k) {
    int p = data.get(k);
    return p >= 0 ? find(p) : k;
  }

  int size(int k) { return (-data.get(find(k))); }

  bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return false;
    auto u = data.get(x);
    auto v = data.get(y);

    if (u < v) {
      auto a = data.mutable_get(x);
      *a += v;
      auto b = data.mutable_get(y);
      *b = x;
    } else {
      auto a = data.mutable_get(y);
      *a += u;
      auto b = data.mutable_get(x);
      *b = y;
    }
    return true;
  }
};
#line 2 "structure/union-find/union-find-undo.hpp"

#include <stack>
#line 6 "structure/union-find/union-find-undo.hpp"

struct UnionFindUndo {
  std::vector<int> data;
  std::stack<std::pair<int, int>> history;

  UnionFindUndo(int sz) { data.assign(sz, -1); }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    history.emplace(x, data[x]);
    history.emplace(y, data[y]);
    if (x == y) return (false);
    if (data[x] > data[y]) std::swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if (data[k] < 0) return (k);
    return (find(data[k]));
  }

  int size(int k) { return (-data[find(k)]); }

  void undo() {
    data[history.top().first] = history.top().second;
    history.pop();
    data[history.top().first] = history.top().second;
    history.pop();
  }

  void snapshot() {
    while (history.size()) history.pop();
  }

  void rollback() {
    while (history.size()) undo();
  }
};
#line 2 "structure/union-find/weighted-union-find.hpp"

#line 5 "structure/union-find/weighted-union-find.hpp"

template <typename T>
struct WeightedUnionFind {
  std::vector<int> data;
  std::vector<T> ws;

  WeightedUnionFind() {}

  WeightedUnionFind(int sz) : data(sz, -1), ws(sz) {}

  int find(int k) {
    if (data[k] < 0) return k;
    auto par = find(data[k]);
    ws[k] += ws[data[k]];
    return data[k] = par;
  }

  T weight(int t) {
    find(t);
    return ws[t];
  }

  bool unite(int x, int y, T w) {
    w += weight(x);
    w -= weight(y);
    x = find(x), y = find(y);
    if (x == y) return false;
    if (data[x] > data[y]) {
      std::swap(x, y);
      w *= -1;
    }
    data[x] += data[y];
    data[y] = x;
    ws[y] = w;
    return true;
  }

  T diff(int x, int y) { return weight(y) - weight(x); }
};
#line 13 "test/unittest/union-finds.test.cpp"

int main() {
  {
    UnionFind uf(6);
    assert(uf.unite(0, 1));
    assert(uf.unite(1, 2));
    assert(!uf.unite(0, 2));
    assert(uf.same(0, 2));
    assert(!uf.same(0, 3));
    assert(uf.size(1) == 3);
    uf.unite(3, 4);
    auto groups = uf.groups();
    for (auto& group : groups) std::sort(group.begin(), group.end());
    std::sort(groups.begin(), groups.end());
    assert((groups == std::vector<std::vector<int>>{{0, 1, 2}, {3, 4}, {5}}));
  }

  {
    WeightedUnionFind<long long> uf(4);
    assert(uf.unite(0, 1, 3));
    assert(uf.unite(1, 2, -5));
    assert(uf.diff(0, 2) == -2);
    assert(uf.diff(2, 0) == 2);
    assert(!uf.unite(0, 2, -2));
  }

  {
    UnionFindUndo uf(4);
    uf.unite(0, 1);
    uf.snapshot();
    uf.unite(1, 2);
    assert(uf.size(0) == 3);
    uf.rollback();
    assert(uf.size(0) == 2);
    assert(uf.size(2) == 1);
    uf.unite(2, 3);
    uf.undo();
    assert(uf.size(2) == 1);
  }

  {
    PartiallyPersistentUnionFind uf(5);
    uf.unite(1, 0, 1);
    uf.unite(3, 1, 2);
    uf.unite(5, 3, 4);
    assert(uf.size(0, 0) == 1);
    assert(uf.size(1, 0) == 2);
    assert(uf.size(2, 2) == 1);
    assert(uf.size(3, 0) == 3);
    assert(uf.find(4, 0) == uf.find(4, 2));
    assert(uf.find(4, 0) != uf.find(4, 3));
  }

  {
    PersistentUnionFind original(5);
    auto first = original;
    first.unite(0, 1);
    auto second = first;
    second.unite(1, 2);
    assert(original.size(0) == 1);
    assert(first.size(0) == 2);
    assert(first.size(2) == 1);
    assert(second.size(0) == 3);
  }

  {
    auto add_edge = [](BipartiteGraph& graph, int n, int u, int v) {
      graph.unite(u, v + n);
      graph.unite(u + n, v);
    };

    BipartiteGraph path(3);
    add_edge(path, 3, 0, 1);
    add_edge(path, 3, 1, 2);
    assert(path.bipartite_graph_coloring());
    assert(path[0] != path[1]);
    assert(path[1] != path[2]);

    BipartiteGraph triangle(3);
    add_edge(triangle, 3, 0, 1);
    add_edge(triangle, 3, 1, 2);
    add_edge(triangle, 3, 2, 0);
    assert(!triangle.bipartite_graph_coloring());
  }
}
Back to top page