可并堆 题解
这是一篇使用 Treap 的题解。其实本质还是随机堆但有些不一样的地方
Treap 是 Tree(树) 与 Heap(堆) 的结合,每个结点有 \(\text{value}\),\(\text{priority}\) 两个属性,对 \(\text{value}\) 它满足二叉搜索树的性质(左儿子的 \(\text{value}\) 不大于自己,右儿子的 \(\text{value}\) 不小于自己);对 \(\text{priority}\) 满足小(大)根堆的性质(任一儿子的 \(\text{priority}\) 不小(大)于自己),在插入结点时随机给一个 \(\text{priority}\) 即可构建一棵期望平衡的二叉搜索树。
插入一个值时,为了维持 Treap 的性质,需要不断旋转。FHQ 大佬对此做了改进,只需对 Treap 进行分裂和合并即可完成平衡树的一系列操作。
Heap... 合并... 这不就是可并堆吗?
于是,x383494 有了一些想法。
为了维护此题中 Heap 的平衡,我们仍然构建一棵 Treap,但将 \(\text{value}\) 赋一个随机值,合并时看两堆 \(\text{value}\) 大小调整左右关系。
下面是用结构体实现的结点:
struct Node{
int pri, val;
Node *ls, *rs;
int siz, time;
bool operator<(Node const& b){
if(pri < b.pri) return true;
if(pri == b.pri && time < b.time) return true;
return false;
}
} nil_ = {0x383494, 0x383494, &nil_, &nil_}, *nil = &nil_;
实际上,FHQ Treap 的所有操作中本题只用到了合并。
下面是修改过的合并操作:
Node *merge(Node *sm, Node *bg){
if(sm->val > bg->val) sd swap(sm, bg); // 这里用 Tree 的随机值维护平衡
if(sm == nil || bg == nil) return sm == nil ? bg : sm;
if(*sm < *bg){
sm->rs = merge(sm->rs, bg);
upd_siz(sm);
bg = sm;
} else {
bg->ls = merge(sm, bg->ls);
upd_siz(bg);
}
return bg;
}
与原合并不同在于前一个堆的 \(\text{value}\) 不一定小于后一个。
code:
会有一些意义不明的注释
// treap
#include <cstdio>
#include <random>
#define UP(i,s,e) for(auto i=s; i!=e; ++i)
#define sd std::
sd mt19937 rdna(0x383494);
namespace FHQ{ // }{{{
struct Node{
int pri, val;
Node *ls, *rs;
int siz, time;
bool operator<(Node const& b){
if(pri < b.pri) return true;
if(pri == b.pri && time < b.time) return true;
return false;
}
} nil_ = {0x383494, 0x383494, &nil_, &nil_}, *nil = &nil_;
Node datas[100000]; int cnt=0;
void upd_siz(Node *root){
root->siz = root->ls->siz + root->rs->siz + 1;
}
Node *merge(Node *sm, Node *bg){
if(sm->val > bg->val) sd swap(sm, bg);
if(sm == nil || bg == nil) return sm == nil ? bg : sm;
if(*sm < *bg){
sm->rs = merge(sm->rs, bg);
upd_siz(sm);
bg = sm;
} else {
bg->ls = merge(sm, bg->ls);
upd_siz(bg);
}
return bg;
}
Node *nnod(int p){
datas[cnt] = { p, (int)rdna(), nil, nil, 1, cnt };
return &datas[cnt++];
}
} // {}}}
namespace m{ // }{{{
FHQ::Node *hps[100000];
int fa[100000], del[100000];
int getfa(int x){
if(fa[x] == x) return x;
return fa[x] = getfa(fa[x]);
}
int merges(int x, int y){
x = getfa(x), y = getfa(y);
if(x == y) return x;
return fa[x] = y;
}
int in, im;
void work(){
scanf("%d%d", &in, &im);
UP(i, 0, in){
int x;
scanf("%d", &x);
hps[i] = FHQ::nnod(x);
fa[i] = i;
}
UP(i, 0, im){
int x, y;
scanf("%d", &x);
if(x == 1){
scanf("%d%d", &x, &y);
x--, y--;
if(del[x] || del[y] || getfa(x) == getfa(y)) continue;
x = getfa(x), y = getfa(y);
hps[x] = hps[y] = FHQ::merge(hps[x], hps[y]);
merges(x, y);
} else {
scanf("%d", &x);
x--;
if(del[x]){
printf("-1\n");
continue;
}
FHQ::Node *hp = hps[getfa(x)];
del[hp->time] = 1;
x = getfa(x);
hps[x] = FHQ::merge(hp->ls, hp->rs);
printf("%d\n", hp->pri);
}
}
}
} // {}}}
int main(){m::work(); return 0;}