平衡树

Treap

Treap 是既满足二叉搜索树的性质又满足堆性质的树。它通过随机优先级实现树高在 \(O(\log(n))\) 级别。以下是两种实现 Treap 的方式。

旋转 Treap

非旋 Treap

又称 FHQ-Treap。
原理:将要修改的部分分裂出来,修改,然后合并。

分裂

void split(int k, int id, int &p, int &q){
	if(!id){
		p = q = 0;
		return;
	}
	pd(id);
	if(tr[tr[id].ls].sz >= k){ //左子树在p中
		split(k, tr[id].ls, p, tr[id].ls);
		q = id;
	}
	else{ //右子树在q中
		split(k - tr[tr[id].ls].sz - 1, tr[id].rs, tr[id].rs, q);
		p = id;
	}
	pp(id);
}

合并

int merge(int p, int q){
	if(!p || !q)
		return p + q;
	if(tr[p].p > tr[q].p){ //p当根
		pd(p);
		tr[p].rs = merge(tr[p].rs, q);
		pp(p);
		return p;
	}
    else{ //q当根
	    pd(q);
	    tr[q].ls = merge(p, tr[q].ls);
	    pp(q);
	    return q;
    }
}

splay

splay 有一字旋和之字旋。
可以证明,其时间复杂度是 \(O(\log_2 n)\)

旋转函数

void rotate(int x){
	int y = tr[x].fa, z = tr[y].fa, k = (tr[y].s[1] == x);
	pd(x);
	pd(y);
	tr[z].s[tr[z].s[1] == y] = x, tr[x].fa = z;
	tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].fa = y;
	tr[x].s[k ^ 1] = y, tr[y].fa = x;
	pp(y);
	pp(x);
}

核心函数

void splay(int x, int k){ //把x旋转到k下面
	while(tr[x].fa != k){
		int y = tr[x].fa, z = tr[y].fa;
		if(z != k){
			if((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) 
				rotate(x);
			else
				rotate(y);
		}
		rotate(x);	
	}
	if(!k)
		root = x;
} 

易错点

1 一个点的 \(sz\) 要设为 \(1\), pushup 时要 \(sz\) 时左右孩子之和加 \(1\)

posted @ 2023-12-26 20:42  louisliang  阅读(2)  评论(0编辑  收藏  举报