ポテンシャル付きUnionFind
init
- 整数 \(N\) と演算(群)を与えると、頂点を\(N\)個生成し全て独立にする
- 計算量は \(Ο(N)\)
potentialized_unionfind
- コンストラクタ。initを呼ぶ
leader
- 頂点 \(k\) が所属する連結集合の代表元を返す
- と同時に経路圧縮する
- 計算量は \(Ο(\alpha (N))\)
same
- 頂点 \(p\) と頂点 \(q\) がその時点で同じ連結成分に属しているかを調べ、同じならtrue、違うならfalseを返す
- 計算量は \(Ο(\alpha (N))\)
- size
- 頂点 \(k\) が所属する連結集合のサイズを返す
- 計算量は \(Ο(1)\)
merge
- 頂点 \(p\) と頂点 \(q\) の間にコスト $d$ の辺を張る(集合を合併する)
- 「\(p\) と \(q\) が異なる連結成分に属している」「\(p\) と \(q\) が同じ連結成分に属し、そのポテンシャルの差が \(d\) である」場合は true 、「\(p\) と \(q\) が同じ連結成分に属し、そのポテンシャルの差が \(d\) ではない」場合は false を返す
- 計算量は \(Ο(\alpha (N))\)
potential
- 頂点 \(k\) のポテンシャルを返す
- 計算量は \(Ο(\alpha (N))\)
diff
- (頂点 \(q\) のポテンシャル) \(-\) (頂点 \(p\) のポテンシャル) を返す
- 計算量は \(Ο(\alpha (N))\)
template<typename T>
class potentialized_unionfind{
// Copyright (c) 2024 0214sh7
// https://github.com/0214sh7/library/
private:
std::vector<int> par,rank,size_;
std::vector<T> pot;
std::function<T(T,T)> op_;
std::function<T(T)> inv_;
T id_;
public:
void init(int N, std::function<T(T,T)> op, std::function<T(T)> inv, T id){
op_ = op;
inv_ = inv;
id_ = id;
par.assign(N,0);
rank.assign(N,0);
size_.assign(N,1);
pot.assign(N,id);
for(int i=0;i<N;i++){
par[i] = i;
}
}
potentialized_unionfind(int N, std::function<T(T,T)> op, std::function<T(T)> inv, T id){
init(N,op,inv,id);
}
int leader(int k){
if(par[k]==k){
return k;
}
int r = leader(par[k]);
pot[k] = op_(pot[par[k]],pot[k]);
par[k] = r;
return par[k];
}
bool same(int p,int q){
return leader(p)==leader(q);
}
int size(int k){
return size_[leader(k)];
}
bool merge(int p, int q, T d){
//pot(Q)-pot(P)=dを満たす
d = op_(potential(p),d);
d = op_(d,inv_(potential(q)));
p = leader(p);
q = leader(q);
if(p==q){
T X = diff(p,q);
return (d == X);
}
if(rank[p]<rank[q]){
std::swap(p,q);
d = inv_(d);
}
par[q]=p;
if(rank[p]==rank[q])rank[p]++;
size_[p] += size_[q];
size_[q] = 0;
pot[q]=d;
return true;
}
T potential(int k){
leader(k);
return pot[k];
}
T diff(int p, int q){
return op_(inv_(potential(p)),potential(q));
}
};
使用例
実行コード
#include <bits/stdc++.h>
template<typename T>
class potentialized_unionfind{/*省略*/};
int main(void){
std::function<long long(long long,long long)> op = [&](long long A, long long B){
return A+B;
};
std::function<long long(long long)> inv = [&](long long A){
return -A;
};
const long long id = 0;
potentialized_unionfind<long long> potUF(6,op,inv,id);
potUF.merge(0,1,1);
potUF.merge(1,2,2);
potUF.merge(2,3,4);
potUF.merge(2,4,8);
std::cout << "頂点 3 と頂点 4 の結合はできているか" << std::endl;
bool c = potUF.same(3,4);
std::cout << (c?"Yes":"No") << std::endl;
std::cout << std::endl;
std::cout << "頂点 3 と頂点 5 の結合はできているか" << std::endl;
bool d = potUF.same(3,5);
std::cout << (d?"Yes":"No") << std::endl;
std::cout << std::endl;
std::cout << "頂点 0 とのポテンシャルの差" << std::endl;
for(int i=0;i<5;i++){
std::cout << potUF.diff(0,i) << " ";
}
std::cout << std::endl << std::endl;
std::cout << "頂点 2 とのポテンシャルの差" << std::endl;
for(int i=0;i<5;i++){
std::cout << potUF.diff(2,i) << " ";
}
std::cout << std::endl;
return 0;
}
出力
頂点 3 と頂点 4 の結合はできているか
Yes
頂点 3 と頂点 5 の結合はできているか
No
頂点 0 とのポテンシャルの差
0 1 3 7 11
頂点 2 とのポテンシャルの差
-3 -2 0 4 8
更新履歴
日時 | 内容 |
---|---|
2024/09/22 | 任意型に対応 |
2024/09/22 | 実装と一部機能の関数名を変更 |
2023/11/11 | long longに対応 |
2023/06/29 | ライセンスのコメントアウトを変更 |
2021/03/26 | 使用例、コンストラクタを追加 |
2020/04/06 | ポテンシャル付きUnionFindを追加 |
確認した問題
問題 | 提出 |
---|---|
Library Checker(行列積) | 提出 |
Library Checker(整数) | 提出 |