$\newcommand{\O}{\mathrm{O}}$

最小全域木を $\O (m \log \log n)$ 時間で求めるアルゴリズム.
Boruvka 法 + Fibonacciheap を用いた Prim 法の高速化 で可能.
ブルーフカ法を $\lceil \log \log n \rceil$ step 進めた後には高々頂点数 $n / \log n$ のグラフになっているのでそこからは高速版の
Prim 法を用いることで残りを $\O (n + m)$ time で求めることができる. よって合計で $\O (m \log \log n)$ time.
もはや当然だが実測が目に見えて速くなることはない.
(関数)
solve(): 最小全域木のコストを返す. 存在しなければ inf を返す.
時間計算量: $\O (m \log \log n)$
template<typename _Key, typename _Tp> class FibonacciHeap
{
public:
using data_type = pair<_Key, _Tp>;
class node
{
public:
data_type _data;
unsigned short int _child;
bool _mark;
node *_par, *_prev, *_next, *_ch_last;
node(data_type&& data) : _data(move(data)), _child(0), _mark(false),
_par(nullptr), _prev(nullptr), _next(nullptr), _ch_last(nullptr){}
inline const _Key& get_key() const noexcept { return _data.first; }
void insert(node *cur){
if(_ch_last) insert_impl(cur, _ch_last);
else _ch_last = cur, _ch_last->_prev = _ch_last->_next = _ch_last;
++_child, cur->_par = this;
}
void erase(node *cur){
if(cur == cur->_prev){
_ch_last = nullptr;
}else{
erase_impl(cur);
if(cur == _ch_last) _ch_last = cur->_prev;
}
--_child, cur->_par = nullptr;
}
};
private:
size_t _size;
node *_minimum;
vector<node*> rank;
static void insert_impl(node *cur, node *next){
cur->_prev = next->_prev, cur->_next = next;
cur->_prev->_next = cur, next->_prev = cur;
}
static void erase_impl(node *cur){
cur->_prev->_next = cur->_next, cur->_next->_prev = cur->_prev;
}
void root_insert(node *cur){
if(_minimum){
insert_impl(cur, _minimum);
if(cur->get_key() < _minimum->get_key()) _minimum = cur;
}else{
_minimum = cur, _minimum->_prev = _minimum->_next = _minimum;
}
}
void root_erase(node *cur){
if(cur == cur->_prev) _minimum = nullptr;
else erase_impl(cur);
}
void _delete(node *cur){
root_erase(cur);
delete cur;
}
template<typename Key, typename Data>
node *_push(Key&& key, Data&& data){
++_size;
data_type new_data(forward<Key>(key), forward<Data>(data));
node* new_node = new node(move(new_data));
root_insert(new_node);
return new_node;
}
void _pop(){
assert(_size > 0);
--_size;
if(_size == 0){
_delete(_minimum);
return;
}
if(_minimum->_ch_last){
for(node *cur = _minimum->_ch_last->_next;;){
node *next = cur->_next;
_minimum->erase(cur), root_insert(cur);
if(!_minimum->_ch_last) break;
cur = next;
}
}
node *next_minimum = _minimum->_next;
for(node*& cur : rank) cur = nullptr;
for(node *cur = next_minimum; cur != _minimum;){
if(cur->get_key() < next_minimum->get_key()) next_minimum = cur;
node *next = cur->_next;
unsigned int deg = cur->_child;
if(rank.size() <= deg) rank.resize(deg + 1, nullptr);
while(rank[deg]){
if(cur->get_key() < rank[deg]->get_key() || cur == next_minimum){
root_erase(rank[deg]), cur->insert(rank[deg]);
}else{
root_erase(cur), rank[deg]->insert(cur);
cur = rank[deg];
}
rank[deg++] = nullptr;
if(rank.size() <= deg) rank.resize(deg + 1, nullptr);
}
rank[deg] = cur;
cur = next;
}
_delete(_minimum);
_minimum = next_minimum;
}
void _decrease_key(node *cur, const _Key& key){
assert(!(key < (_Key)0));
node *change = ((cur->_data.first -= key) < _minimum->get_key()) ? cur : nullptr;
if(!cur->_par || !(cur->get_key() < cur->_par->get_key())){
if(change) _minimum = change;
return;
}
while(true){
node *next = cur->_par;
next->erase(cur), root_insert(cur);
cur->_mark = false, cur = next;
if(!cur->_par) break;
if(!cur->_mark){
cur->_mark = true;
break;
}
}
if(change) _minimum = change;
}
void clear_dfs(node *cur){
if(cur->_ch_last){
for(node *_cur = cur->_ch_last->_next;;){
node *next = _cur->_next;
if(_cur == cur->_ch_last){
clear_dfs(_cur);
break;
}else{
clear_dfs(_cur);
}
_cur = next;
}
}
delete cur;
return;
}
void _clear(){
if(!_minimum) return;
for(node *cur = _minimum->_next;;){
node *next = cur->_next;
if(cur == _minimum){
clear_dfs(cur);
break;
}else{
clear_dfs(cur);
}
cur = next;
}
}
public:
FibonacciHeap() noexcept : _size(0u), _minimum(nullptr){}
// ~FibonacciHeap(){ _clear(); }
inline bool empty() const noexcept { return (_size == 0); }
inline size_t size() const noexcept { return _size; }
inline const data_type& top() const noexcept { return _minimum->_data; }
template<typename Key, typename Data>
node *push(Key&& key, Data&& data){ return _push(forward<Key>(key), forward<Data>(data)); }
void pop(){ _pop(); }
void decrease_key(node *cur, const _Key& key){ _decrease_key(cur, key); }
void clear(){ _clear(); _size = 0; rank.~vector<node*>(); }
};
class UnionFind {
private:
int sz;
vector<int> par, nrank;
public:
UnionFind(){}
UnionFind(int node_size) : sz(node_size), par(sz), nrank(sz, 0){
iota(par.begin(), par.end(), 0);
}
int find(int x){
if(par[x] == x) return x;
else return par[x] = find(par[x]);
}
int unite(int x, int y){
if(nrank[x] < nrank[y]) swap(x,y);
par[y] = x;
if(nrank[x] == nrank[y]) nrank[x]++;
return x;
}
};
template<typename T> class RapidMST {
private:
struct edge {
int to;
T cost;
typename list<edge>::iterator rev;
edge(const int _to, const T _cost)
: to(_to), cost(_cost){}
edge(const int _to, const T _cost, const typename list<edge>::iterator _rev)
: to(_to), cost(_cost), rev(_rev){}
};
struct info {
T cost;
typename list<edge>::iterator itr;
info(){}
info(const T _cost) : cost(_cost){}
info(const T _cost, const typename list<edge>::iterator _itr) : cost(_cost), itr(_itr){}
};
const int V;
const T inf;
UnionFind uf;
vector<list<edge> > G;
void cleanup(vector<int>& rem_node, vector<info>& check){
vector<int> tmp;
for(int v : rem_node){
for(auto it = G[v].begin(); it != G[v].end();){
const edge& e = *it;
const int p = uf.find(e.to);
if(p == v){
it = G[v].erase(it);
continue;
}
if(e.cost < check[p].cost){
if(check[p].cost == inf) tmp.push_back(p);
else G[p].erase(check[p].itr->rev), G[v].erase(check[p].itr);
check[p].cost = e.cost, check[p].itr = it++;
}else{
G[p].erase(e.rev), it = G[v].erase(it);
}
}
for(int id : tmp) check[id].cost = inf;
tmp.clear();
}
}
bool contract(vector<int>& rem_node, vector<int>& check){
vector<int> new_rem_node;
bool update = false;
for(int v : rem_node){
const int p = uf.find(v);
if(G[p].empty() || p != v) continue;
T mn = inf; int id = -1;
for(const edge& e : G[v]){
if(e.cost < mn) mn = e.cost, id = e.to;
}
id = uf.find(id);
if(id != v){
ans += mn, update = true, uf.unite(id, v);
}
}
for(int v : rem_node){
const int p = uf.find(v);
if(v == p) new_rem_node.push_back(v);
else G[p].splice(G[p].begin(), move(G[v]));
}
swap(rem_node, new_rem_node);
return update;
}
int boruvka(vector<int>& rem_node){
vector<int> erase_check(V, 0);
iota(rem_node.begin(), rem_node.end(), 0);
vector<info> multiple_check(V);
const int cnt = ceil(log2(ceil(log2(V)) + 1)) + 1;
for(int i = 0; i < V; ++i) multiple_check[i] = info(inf);
for(int i = 0; i < cnt; ++i){
if((int)rem_node.size() == 1) return 1;
if(!contract(rem_node, erase_check)) return 0;
cleanup(rem_node, multiple_check);
}
return 2;
}
void prim(vector<int>& rem_node){
int remV = 0;
vector<int> trans(V);
for(int v : rem_node) trans[v] = remV++;
vector<T> d(remV, inf);
FibonacciHeap<T, int> fheap;
vector<typename FibonacciHeap<T, int>::node*> nodes(remV, nullptr);
d[0] = 0;
nodes[0] = fheap.push(0, 0);
while(!fheap.empty()){
const int v = fheap.top().second;
ans += fheap.top().first;
fheap.pop();
d[v] = -1;
for(auto& e : G[rem_node[v]]){
const int p = trans[uf.find(e.to)];
if(d[p] >= 0 && d[p] > e.cost){
if(d[p] == inf){
nodes[p] = fheap.push(e.cost, p);
}else{
fheap.decrease_key(nodes[p], d[p] - e.cost);
}
d[p] = e.cost;
}
}
}
}
public:
T ans;
RapidMST(const int node_size)
: V(node_size), inf(numeric_limits<T>::max()), uf(V), G(V), ans(0){}
void add_edge(const int from, const int to, const T cost){
G[from].emplace_back(to, cost);
G[to].emplace_back(from, cost, --G[from].end());
G[from].back().rev = --G[to].end();
}
T solve(){
vector<int> rem_node(V);
const int res = boruvka(rem_node);
if(res == 0) return inf;
else if(res == 1) return ans;
prim(rem_node);
return ans;
}
};
AOJ : Minimum Spanning Tree 提出コード