Skip to content

Commit 5dd2cae

Browse files
committed
potentialized-unionfind
1 parent 1a13fd2 commit 5dd2cae

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed

cpp/potentialized-unionfind.hpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#pragma once
2+
3+
/**
4+
* @file potentialized-unionfind.hpp
5+
* @brief ポテンシャル付きUnionFind
6+
*/
7+
#include <cassert>
8+
#include <functional>
9+
#include <stack>
10+
#include <utility>
11+
#include <vector>
12+
13+
/**
14+
* @brief ポテンシャル付きUnionFind
15+
* @tparam S ポテンシャルの型
16+
* @tparam Op Sの積のファンクタ
17+
* @tparam E Sの単位元を返すファンクタ
18+
* @tparam Inv Sの逆元を返すファンクタ
19+
*/
20+
template <typename S, class Op, class E, class Inv>
21+
class PotentializedUnionFind {
22+
private:
23+
int _n;
24+
// 負ならサイズ、非負なら親
25+
std::vector<int> parent_or_size;
26+
// 重み
27+
std::vector<S> diff_weight;
28+
29+
inline constexpr static auto op = Op();
30+
inline constexpr static auto e = E();
31+
inline constexpr static auto inv = Inv();
32+
33+
public:
34+
PotentializedUnionFind() : _n{}, parent_or_size{}, diff_weight{} {}
35+
36+
/**
37+
* @param n 要素数
38+
*/
39+
explicit PotentializedUnionFind(int n) : _n(n), parent_or_size(n, -1), diff_weight(n) {}
40+
41+
/**
42+
* @brief 頂点aの属する連結成分の代表元
43+
*/
44+
int leader(int a) {
45+
assert(0 <= a && a < _n);
46+
if (parent_or_size[a] < 0) return a;
47+
std::stack<int> stk;
48+
stk.push(a);
49+
while (parent_or_size[stk.top()] >= 0) {
50+
stk.push(parent_or_size[stk.top()]);
51+
}
52+
const int root = stk.top();
53+
stk.pop();
54+
stk.pop();
55+
while (!stk.empty()) {
56+
diff_weight[stk.top()] = op(diff_weight[stk.top()], diff_weight[parent_or_size[stk.top()]]);
57+
parent_or_size[stk.top()] = root;
58+
stk.pop();
59+
}
60+
return root;
61+
}
62+
63+
/**
64+
* @brief a のグループと b のグループを統合する
65+
* @param w (b のポテンシャル) - (a のポテンシャル)
66+
* @return 連結したものの代表元
67+
*/
68+
int merge(int a, int b, S w) {
69+
assert(0 <= a && a < _n);
70+
assert(0 <= b && b < _n);
71+
w = op(w, op(weight(a), inv(weight(b))));
72+
int x = leader(a), y = leader(b);
73+
if (x == y) return x;
74+
if (-parent_or_size[x] < -parent_or_size[y]) {
75+
std::swap(x, y);
76+
w = -w;
77+
}
78+
parent_or_size[x] += parent_or_size[y];
79+
parent_or_size[y] = x;
80+
diff_weight[y] = w;
81+
return x;
82+
}
83+
84+
/**
85+
* @brief 頂点a,bが連結かどうか
86+
*/
87+
bool same(int a, int b) {
88+
assert(0 <= a && a < _n);
89+
assert(0 <= b && b < _n);
90+
return leader(a) == leader(b);
91+
}
92+
93+
/**
94+
* @brief (b のポテンシャル) - (a のポテンシャル)
95+
* @remark デフォルトコンストラクタで作られる S について Inv が定義されているならば、a == b を許容
96+
*/
97+
S diff(int a, int b) {
98+
assert(same(a, b));
99+
return op(weight(b), inv(weight(a)));
100+
}
101+
102+
/**
103+
* @brief 頂点aの属する連結成分のサイズ
104+
*/
105+
int size(int a) {
106+
assert(0 <= a && a < _n);
107+
return -parent_or_size[leader(a)];
108+
}
109+
110+
/**
111+
* @brief グラフを連結成分に分け、その情報を返す
112+
* @return 「一つの連結成分の頂点番号のリスト」のリスト
113+
*/
114+
std::vector<std::vector<int>> groups() {
115+
std::vector<int> leader_buf(_n), group_size(_n);
116+
for (int i = 0; i < _n; i++) {
117+
leader_buf[i] = leader(i);
118+
group_size[leader_buf[i]]++;
119+
}
120+
std::vector<std::vector<int>> result(_n);
121+
for (int i = 0; i < _n; i++) {
122+
result[i].reserve(group_size[i]);
123+
}
124+
for (int i = 0; i < _n; i++) {
125+
result[leader_buf[i]].push_back(i);
126+
}
127+
result.erase(std::remove_if(result.begin(), result.end(), [&](const std::vector<int>& v) { return v.empty(); }), result.end());
128+
return result;
129+
}
130+
131+
private:
132+
S weight(int a) {
133+
leader(a);
134+
return diff_weight[a];
135+
}
136+
};
137+
138+
namespace potentialized_unionfind {
139+
template <typename S>
140+
struct Zero {
141+
S operator()() const { return S(0); }
142+
};
143+
template <typename S>
144+
struct One {
145+
S operator()() const { return S(1); }
146+
};
147+
template <typename S>
148+
struct None {
149+
S operator()() const { return S{}; }
150+
};
151+
template <typename S>
152+
struct Div {
153+
S operator()(const S& a) const { return S(1) / a; }
154+
};
155+
} // namespace potentialized_unionfind
156+
/**
157+
* @tparam S 群の型
158+
*/
159+
template <typename S>
160+
using UnionFindPlus = PotentializedUnionFind<S, std::plus<S>, potentialized_unionfind::None<S>, std::negate<S>>;
161+
/**
162+
* @tparam S 群の型
163+
*/
164+
template <typename S>
165+
using UnionFindMul = PotentializedUnionFind<S, std::multiplies<S>, potentialized_unionfind::One<S>, potentialized_unionfind::Div<S>>;

test/aoj-dsl-1-b.test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/DSL_1_B"
2+
3+
#include <iostream>
4+
5+
#include "../cpp/potentialized-unionfind.hpp"
6+
7+
int main() {
8+
int n, q;
9+
std::cin >> n >> q;
10+
UnionFindPlus<long long> uf(n);
11+
while (q--) {
12+
int t;
13+
std::cin >> t;
14+
if (t == 0) {
15+
int x, y, z;
16+
std::cin >> x >> y >> z;
17+
uf.merge(x, y, z);
18+
} else {
19+
int x, y;
20+
std::cin >> x >> y;
21+
if (uf.same(x, y))
22+
std::cout << uf.diff(x, y) << std::endl;
23+
else
24+
std::cout << '?' << std::endl;
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)