可并堆 题解

题目传送门

这是一篇使用 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;}
posted @ 2023-06-09 10:56  383494  阅读(9)  评论(0编辑  收藏  举报