通过oi-wiki学习了并查集(disjoint set union , DSU)
#include <bits/stdc++.h>
#include <numeric>
#include <vector>
using namespace std;
struct dsu {
vector<size_t> pa , size;
explicit dsu( size_t size_ ):pa( size_*2 ), size( size_*2 , 1 ){
iota( pa.begin() , pa.begin()+size_ , size_);
iota( pa.begin()+size_ , pa.end() , size_);
}
size_t find( size_t x ) ;
void unite( size_t x , size_t y ) ;
void erase( size_t x ) ;
void move( size_t x , size_t y ) ;
};
//normal find without compressing
//size_t dsu::find ( size_t x ){ return pa[x] == x ? x : find( pa[x] ) ; }
size_t dsu::find ( size_t x ){ return pa[x] == x ? x : pa[x] = find( pa[x] ) ; }
//路径压缩:将所有节点直接归属于根节点
//normal unite
//void dsu::unite(size_t x, size_t y){ pa[find(x)] = find(y); }
//在路径压缩可行时,启发式合并优化并没有太明显差距;
//在可持久化并查集、线段树分治 + 并查集中,一般使用只启发式合并的并查集。
void dsu::unite(size_t x, size_t y){
x = find(x) , y = find(y) ;
if( x == y ) return ;
if( size[x] < size[y] ) swap( x , y ) ;
pa[y] = x ; //core:将size小的分支根节点改为size大的分支根节点
size[x] += size[y] ;
}
// 创建一个副本来保存原有的树林结构,前半段的父亲为后半段对应,删除时将父亲指向自己
void dsu::erase(size_t x){
--size[find(x)] ;
pa[x] = x ;
}
void dsu::move(size_t x, size_t y){
size_t fx = find(x) , fy = find(y) ;
if( fx == fy ) return ;
pa[x] = fy ;
--size[fx] , ++size[fy] ;
}