$\newcommand{\O}{\mathrm{O}}$
lookup, insert, erase などの操作をならし計算量 $\O (\log n)$ で実現する平衡二分探索木([Sleator, Tarjan '83]).
動的連結性クエリに高速に答える手法(Link Cut Tree)を考案する過程で得られたデータ構造.
以下では遅延処理(区間加算, 区間総和)を施した実装にしている.
時間計算量: 各クエリ $\O (\log n)$ (ならし計算量)
template<typename _Key, typename _Tp> class node { public: int sz; _Key key; _Tp val, al, lazy; node *left, *right, *par; node(_Key _key, _Tp _val) noexcept : sz(1), key(_key), val(_val), al(val), lazy(id1), left(nullptr), right(nullptr), par(nullptr){} static const _Tp id1 = (_Tp)0; static const _Tp id2 = (_Tp)0; static void opr1(_Tp& arg1, const _Tp arg2){ arg1 += arg2; } static _Tp opr2(const _Tp arg1, const _Tp arg2){ return arg1 + arg2; } inline bool isRoot() const { return !par; } void push(){ if(lazy != id1){ opr1(val, lazy), al += lazy * sz; if(left) opr1(left->lazy, lazy); if(right) opr1(right->lazy, lazy); lazy = id1; } } void eval(){ sz = 1, al = val; if(left) left->push(), sz += left->sz, al = opr2(left->al, al); if(right) right->push(), sz += right->sz, al = opr2(al, right->al); } void rotate(bool right_){ node<_Key, _Tp> *p = par, *g = p->par; if(right_){ if((p->left = right)) right->par = p; right = p, p->par = this; }else{ if((p->right = left)) left->par = p; left = p, p->par = this; } p->eval(), eval(); if(!(par = g)) return; if(g->left == p) g->left = this; if(g->right == p) g->right = this; g->eval(); } }; template<typename _Key, typename _Tp> int size(node<_Key, _Tp>* u){ return u ? u->sz : 0; } // 頂点 u を木の根にする template<typename _Key, typename _Tp> node<_Key, _Tp>* splay(node<_Key, _Tp>* u) noexcept { if(!u) return nullptr; while(!(u->isRoot())){ node<_Key, _Tp> *p = u->par, *gp = p->par; if(p->isRoot()){ // zig p->push(), u->push(); u->rotate((u == p->left)); }else{ gp->push(), p->push(), u->push(); bool flag = (u == p->left); if((u == p->left) == (p == gp->left)){ // zig-zig p->rotate(flag), u->rotate(flag); }else{ // zig-zag u->rotate(flag), u->rotate(!flag); } } } u->push(); return u; } // root を根とする木の頂点で k 番目に小さいキー値のものを探索する // 返り値は (木の根, k 番目が存在するかしないか(0/1)) template<typename _Key, typename _Tp> pair<node<_Key, _Tp>*, bool> get(const int k, node<_Key, _Tp>* root) noexcept { if(size(root) < k) return make_pair(root, false); int sum = 0; node<_Key, _Tp> *cur = nullptr, *nx = root; while(nx){ cur = nx, cur->push(); if(size(cur->left) < sum - 1){ nx = cur->right, sum -= size(cur->left) + 1; }else if(size(cur->left) >= sum){ nx = cur->left; }else{ return make_pair(splay(cur), true); } } } // root を根とする木の頂点でキーが _key であるものを探索する // 返り値は (木の根, 存在するかしないか(0/1)) template<typename _Key, typename _Tp> pair<node<_Key, _Tp>*, bool> find(const _Key _key, node<_Key, _Tp>* root) noexcept { node<_Key, _Tp> *cur = nullptr, *nx = root; while(nx){ cur = nx, cur->push(); if(cur->key == _key) return make_pair(splay(cur), true); if(cur->key > _key) nx = cur->left; else nx = cur->right; } return make_pair(splay(cur), false); } // root を根とする木の頂点でキーが _key 以上で最小のものを返す(bool は _key 以上のものがあるかないか) // _key 以上のものがなくても返り値のノードは nullptr とは限らないことに注意(_key 以上がなくても木の根は変わるので) template<typename _Key, typename _Tp> pair<node<_Key, _Tp>*, bool> lower_bound(const _Key _key, node<_Key, _Tp>* root) noexcept { node<_Key, _Tp> *cur = nullptr, *nx = root, *res = nullptr; while(nx){ cur = nx, cur->push(); if(cur->key >= _key){ nx = cur->left; if(!res || cur->key <= res->key) res = cur; }else nx = cur->right; } node<_Key, _Tp>* tmp = splay(cur); return res ? make_pair(splay(res), true) : make_pair(tmp, false); } // root を根とする木の頂点でキーが _key を超える最小のものを返す(bool は _key を超えるものがあるかないか) // _key を超えるものがなくても返り値のノードは nullptr とは限らないことに注意(_key を超えるものがなくても木の根は変わるので) template<typename _Key, typename _Tp> pair<node<_Key, _Tp>*, bool> upper_bound(const _Key _key, node<_Key, _Tp>* root) noexcept { node<_Key, _Tp> *cur = nullptr, *nx = root, *res = nullptr; while(nx){ cur = nx, cur->push(); if(cur->key > _key){ nx = cur->left; if(!res || cur->key <= res->key) res = cur; }else nx = cur->right; } node<_Key, _Tp>* tmp = splay(cur); return res ? make_pair(splay(res), true) : make_pair(tmp, false); } // root1 を根とする木のすべての要素が root 2 を根とする木のすべての要素より小さいときの merge template<typename _Key, typename _Tp> node<_Key, _Tp>* join(node<_Key, _Tp>* root1, node<_Key, _Tp>* root2) noexcept { if(!root1 || !root2) return root1 ? root1 : root2; node<_Key, _Tp> *cur = nullptr, *nx = root1; while(nx) cur = nx, cur->push(), nx = cur->right; node<_Key, _Tp>* ver = splay(cur); ver->right = root2, ver->eval(), root2->par = ver; return ver; } // root を根とする木に ver を insert する template<typename _Key, typename _Tp> node<_Key, _Tp>* insert(node<_Key, _Tp>* ver, node<_Key, _Tp>* root) noexcept { if(!root) return ver; node<_Key, _Tp> *cur = nullptr, *nx = root; while(nx){ cur = nx, cur->push(); if(cur->key > ver->key) nx = cur->left; else nx = cur->right; } if(cur->key > ver->key) cur->left = ver, ver->par = cur; else cur->right = ver, ver->par = cur; cur->eval(); return splay(ver); } // root を根とする木から _key をキーとする要素を erase する(同一キーの要素が複数ある場合はどれかひとつだけ消す) // _key をキーとする要素がない場合は第 2 引数として false を返す. template<typename _Key, typename _Tp> pair<node<_Key, _Tp>*, bool> erase(const _Key _key, node<_Key, _Tp>* root){ pair<node<_Key, _Tp>*, bool> ver = find(_key, root); assert(ver.second); node<_Key, _Tp> *l = ver.first->left, *r = ver.first->right; if(l) l->par = nullptr; if(r) r->par = nullptr; delete ver.first; return make_pair(join(l, r), true); } // root を根とする木を (_key 未満, _key 以上) の木に split する. // lower_bound ⇒ get に変えるとキー値ベースではなく個数ベースで split できる. template<typename _Key, typename _Tp> pair<node<_Key, _Tp>*, node<_Key, _Tp>*> split_key(const _Key _key, node<_Key, _Tp>* root) noexcept { pair<node<_Key, _Tp>*, bool> res = lower_bound(_key, root); if(!res.second) return make_pair(res.first, nullptr); node<_Key, _Tp> *res1 = nullptr, *res2 = res.first; res1 = res2->left, res2->left = nullptr, res2->eval(); if(res1) res1->par = nullptr; return make_pair(res1, res2); } // root を根とする木のうちキー値が [lkey, rkey) のものに対して x の更新を行う. template<typename _Key, typename _Tp> node<_Key, _Tp>* range(_Key lkey, _Key rkey, _Tp x, node<_Key, _Tp>* root) noexcept { auto pnn1 = split_key(lkey, root); auto pnn2 = split_key(rkey, pnn1.second); if(pnn2.first) node<_Key, _Tp>::opr1(pnn2.first->lazy, x); return join(pnn1.first, join(pnn2.first, pnn2.second)); } // root を根とする木のうちキー値が [lkey, rkey) のものに対するクエリに答える. // 返り値は(木の根, 値) template<typename _Key, typename _Tp> pair<node<_Key, _Tp>*, _Tp> query(_Key lkey, _Key rkey, node<_Key, _Tp>* root) noexcept { auto pnn1 = split_key(lkey, root); auto pnn2 = split_key(rkey, pnn1.second); _Tp res = (pnn2.first ? pnn2.first->al : node<_Key, _Tp>::id2); return make_pair(join(pnn1.first, join(pnn2.first, pnn2.second)), res); }
Atcoder : データ構造
提出コード(非想定解)
Atcoder : Hash Swapping
提出コード