左偏树模板

题目链接

定义

\qquad 左偏树是一棵二叉树,具有的性质,支持在 O ( log ⁡ 2 n ) O(\log_2n) O(log2n)的时间复杂度内进行合并的数据结构

一些定义

外结点:左儿子或右儿子为空的结点
距离:一个结点的距离为该结点到最近的外结点的距离
\qquad 我们将空结点距离定义为 − 1 -1 1

基本性质

左偏树具有性质,即对于每个结点 x x x v x < v l c v_x<v_{lc} vx<vlc v x < v r c v_x<v_{rc} vx<vrc
左偏树具有左偏性质,即对于每个节点 x x x d i s t l c ≥ d i s t r c dist_{lc} \ge dist_{rc} distlcdistrc

结论

1. 结点 x x x的距离 d i s t x = d i s t r c + 1 dist_x=dist_{rc} + 1 distx=distrc+1
2. 距离为 n n n的左偏树至少有 2 n + 1 − 1 2^{n + 1}-1 2n+11个结点。此时该左偏树的形态是一棵满二叉树
3. 结点数为 n n n的左偏树,其距离不超过 log ⁡ n + 1 − 1 \log_{n + 1} - 1 logn+11

左偏树的操作

1. 合并
\qquad 合并两个左偏树 x , y x,y x,y,先考虑维护堆的性质
∙ \qquad\quad\bullet v x ≤ v y v_x\leq v_y vxvy则将 x x x作为新树的根 v x > v y v_x>v_y vx>vy s w a p ( v x , v y ) swap(v_x,v_y) swap(vx,vy)避免讨论
∙ \qquad\quad\bullet y y y x x x任意一个儿子合并
∙ \qquad\quad\bullet 重复以上操作,直至 x , y x,y x,y中有一个为空结点
但是这样的合并操作可能会让合并的树退化为链,复杂度退化为 O ( n ) O(n) O(n)为了让时间复杂度更优,就要让树变得更加平衡,有以下两种方法
\qquad\quad 1. 每次合并都随机与左右儿子合并
\qquad\quad 2. 左偏树
左偏树中左儿子的距离大于右儿子的距离,我们每次 y y y x x x的右儿子合并,由于左偏树的树高是 log ⁡ 2 n \log_2n log2n的,所以单次合并的时间复杂度也是 O ( log ⁡ 2 n ) O(\log_2n) O(log2n)\

维护左偏

每次合并后判断 d i s t l c < d i s t r c dist_{lc}<dist_{rc} distlc<distrc则交换 l c , r c lc,rc lc,rc,并维护 d i s t x = d i s t r c + 1 dist_x=dist_{rc} + 1 distx=distrc+1

int merge(int x,int y){
	if(!x || !y) return x + y;
	if(v[y] < v[x]) swap(v[x],v[y]);
	rc[x] = merge(rc[x],y);
	if(dist[lc[x]] < dist[rc[x]]) swap(lc[x],rc[x]);
	dist[x] = dist[rc[x]] + 1}

2. 插入
\qquad 插入就是将新节点与左偏树合并

3. 删除
\qquad 删除就是合并目标节点左右儿子
4. 查询最小值
\qquad 左偏树具有堆的性质,最小值就是根节点
5. 查询某个结点所在左偏树的根节点
\qquad 和并查集很像,用一个数组 r f rf rf来存储结点的父节点,用路径压缩来减少时间复杂度

int find(int x){
	return rf[x] == x ? x : rf[x] = find(rf[x]);
}

附上完全代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 1e5 + 10;

struct Node{
	int v,id;
	bool operator <(Node x) const {return v == x.v ? id < x.id : v < x.v;}
}v[MAXN];

int rc[MAXN],lc[MAXN],rt[MAXN],tf[MAXN],dist[MAXN];

inline int read(){
	int n = 0,l = 1;
	char c = getchar();
	while(c < '0' || c > '9'){
		if(c == '-') l = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9'){
		n = (n << 1) + (n << 3) + (c & 15);
		c = getchar();
	}
	return n * l;
}

int merge(int x,int y){
	if(!x || !y) return x + y;
	if(v[y] < v[x]) swap(x,y);
	rc[x] = merge(rc[x],y);
	if(dist[lc[x]] < dist[rc[x]]) swap(lc[x],rc[x]);
	dist[x] = dist[rc[x]] + 1;
	return x;
}

int find(int x){
	return rt[x] == x ? x : rt[x] = find(rt[x]);
}

int main(){
	dist[0] = -1;
	memset(tf,0,sizeof(tf));
	int n = read(),m = read();
	for(int i = 1; i <= n; i ++){
		v[i].v = read();
		v[i].id = i;
		rt[i] = i;
	}
	while(m --){
		int op = read(),x = read();
		if(op == 1){
			int y = read();
			if(tf[x] || tf[y]) continue;
			x = find(x);
			y = find(y);
			if(x != y) rt[x] = rt[y] = merge(x,y);
		}
		else{
			if(tf[x]){
				printf("-1\n");
				continue;
			}
			x = find(x);
			printf("%d\n",v[x].v);
			tf[x] = 1;
			rt[lc[x]] = rt[rc[x]] = rt[x] = merge(lc[x],rc[x]);
			lc[x] = rc[x] = dist[x] = 0;
		}
	}
	return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值