0
点赞
收藏
分享

微信扫一扫

无旋treap学习笔记

jjt二向箔 2022-01-17 阅读 57
数据结构

目录

介绍

背景

原理

声明

Pushup

Split

Merge

例题

P3369 【模板】普通平衡树


介绍

无旋treap ,又称为 非旋treap , fhq_treap ,是一个非常实用的数据结构,用来优化一般的 二叉搜索树 所解决不了的问题,大大提高了时间的效率,在解决平衡树问题的应用较广。

其中用处相同的还有 splay ,但 splay 代码码量大,本蒟蒻一年前开始学,现在连无旋treap都会了,还不会splay,但是splay是 LCT 的重要前置知识,所以两种数据结构都有学的必要性。

背景

一般的二叉搜索树 (指某节点的左子树权值均比节点权值大(或小),右子树权值均比节点小(或大)的一颗二叉树) 的主要作用是查询第 kk 大的权值或权值 xx 的排名,一般的时间复杂度为 O(n log n)O(nlogn) ,但是数据可以将它卡成一条链,此时的时间复杂度为 O(n^2)O(n2)

可以看出,当这一颗二叉搜索树的形状越扁(即 深度越小 )时,其时间复杂度就会更优,于是平衡树应运而生。

原理

无旋treap,肯定和常规数据结构(指splay)不一样,它是不用旋转的(少了毒瘤操作真开心QwQ)。

treap=tree+heap(即“树”+“堆”) 所以treap既有堆的性质,又有二叉搜索树的性质,而其中堆的性质,就是我们把树拍扁的关键。

无旋treap的核心操作有两个,分别为 分裂(split) 和 合并(merge) ,分裂就是将一棵树掰开两瓣,合并就是将两棵树合为一颗树。

声明

struct node{
	int ls;//左儿子 
	int rs;//右儿子 
	int sz;//子树大小 
	int val;//节点权值 
	int id;//编号(维护堆的性质) 
}tree[2000200];

Pushup

不必赘述,将左右子树的 $size$ 加起来即可

void pushup(int x)
{
    tree[x].sz=tree[ls(x)].sz+tree[rs(x)].sz+1;	
} 

Split

假设将treap掰成根为 $a$ 和根为 $b$ 的两颗treap,其中 $a$ 子树权值小于等于(大于等于)   $val$ ,$b$ 子树权值大于(小于) $val$ ,假设原treap根为 $rt$ 。

若 $tree[rt].val \le val$ 则证明 $rt$ 的左子树所有的点都可以放在 aa 左子树上,问题就变为: 将根为 $tree[rt].rs$ 的树分为根为 $tree[a].rs$ 和根为 $b$ 的树,可以递归求解。

$tree[rt].val > val$ 同理,证明 $rt$ 的右子树所有点都可以放到 $b$ 右子树上,问题变为:将根为 $tree[rt].ls$ 的树分为根为 $a$ 和根为 $tree[b].ls$ 的树,同样可以递归求解。

void split(int rt,int &x,int &y,int val)
{
	if(rt==0)
	{
		x=0,y=0;
		return;
	}
	if(tree[rt].val<=val)//此时将rt左子树放到x左子树上 
	{
		x=rt;
		split(rs(rt),rs(x),y,val);
	}
	else//此时将rt右子树放到y右子树上 
	{
		y=rt;
		split(ls(rt),x,ls(y),val);
	}
	pushup(rt);
}

Merge

假设将根为 $a$ 的树和根为 $b$ 的树合并成根为 $rt$ 的树,且树 $a$ 所有权值严格小于树 $b$ 。

很明显,我们要么把树 $a$ 的左子树接到 $rt$ 左边,要么把树 $b$ 的右子树接到 $rt$ 右边,同样可以递归。

我们可以按照设定的 $id$ 值(取随机数)来维护堆的性质,维护一个大根(小根)堆,判断是先放 $a$ 还是先放 $b$ 。

void merge(int &rt,int x,int y)
{
	if(x==0||y==0)
	{
		rt=x|y;
		return;
	}
	if(tree[x].id<tree[y].id)
	{
		rt=x;
		merge(rs(rt),rs(x),y);
	}
	else
	{
		rt=y;
		merge(ls(rt),x,ls(y));
	}
	pushup(rt);
}

有了核心操作,剩下各种诸如插入,删除,求第 $k$ 大的权值等操作都变得肥肠简单~~

具体的操作可以看例题

例题

P3369 【模板】普通平衡树

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ls(x) tree[x].ls
#define rs(x) tree[x].rs
using namespace std;
struct node{
	int ls,rs,sz,val,id;
}tree[200200];
int n,root,tot,opt,zlz;
unsigned int seed=1919810;
inline unsigned int rad()
{
	seed=seed*(unsigned int)100000007;
	return seed;
}
void pushup(int x)
{
    tree[x].sz=tree[ls(x)].sz+tree[rs(x)].sz+1;	
} 
void split(int p,int &x,int &y,int val)
{
	if(p==0)
	{
		x=0,y=0;
		return;
	}
	if(tree[p].val<=val)
	{
		x=p;
		split(rs(p),rs(x),y,val);
	}
	else
	{
		y=p;
		split(ls(p),x,ls(y),val);
	}
	pushup(p);
}
void merge(int &p,int x,int y)
{
	if(x==0||y==0)
	{
		p=x|y;
		return;
	}
	if(tree[x].id<tree[y].id)
	{
		p=x;
		merge(rs(p),rs(x),y);
	}
	else
	{
		p=y;
		merge(ls(p),x,ls(y));
	}
	pushup(p);
}
void build(int x)
{
	tot++;
	ls(tot)=0,rs(tot)=0;
	tree[tot].sz=1,tree[tot].val=x;
	tree[tot].id=rad();
}
int find_max(int x)
{
	while(rs(x))
	x=rs(x);
	return tree[x].val;
}
int find_min(int x)
{
	while(ls(x))
	x=ls(x);
	return tree[x].val;
}
void ins(int x)
{
	build(x);
	int r1=0,r2=0,r3=tot;
	split(root,r1,r2,x);
	merge(r1,r1,r3);
	merge(r1,r1,r2);
	root=r1;
	return;
}
void del(int x)
{
	int r1=0,r2=0,r3=0;
	split(root,r1,r2,x);
	split(r1,r1,r3,x-1);
	merge(r3,ls(r3),rs(r3));
	merge(r1,r1,r3);
	merge(r1,r1,r2);
	root=r1;
	return;
}
int rk1(int x)
{
	int r1=0,r2=0,ans=0;
	split(root,r1,r2,x-1);
	ans=tree[r1].sz+1;
	merge(r1,r1,r2);
	root=r1;
	return ans;
}
int rk2(int rt,int x)
{
	if(tree[ls(rt)].sz+1==x)
	return tree[rt].val;
	if(tree[ls(rt)].sz<x)
	return rk2(rs(rt),x-tree[ls(rt)].sz-1);
	else
	return rk2(ls(rt),x);
}
int lst(int x)
{
	int r1=0,r2=0;
	split(root,r1,r2,x-1);
	int ans=find_max(r1);
	merge(r1,r1,r2);
	root=r1;
	return ans;
}
int nxt(int x)
{
	int r1=0,r2=0;
	split(root,r1,r2,x);
	int ans=find_min(r2);
	merge(r1,r1,r2);
	root=r1;
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d",&opt,&zlz);
		if(opt==1) ins(zlz);
		if(opt==2) del(zlz);
		if(opt==3) printf("%d\n",rk1(zlz));
		if(opt==4) printf("%d\n",rk2(root,zlz));
		if(opt==5) printf("%d\n",lst(zlz));
		if(opt==6) printf("%d\n",nxt(zlz));
	}
}
举报

相关推荐

无旋平衡树

【模板】FHQ Treap

Treap(数组版)

平衡树详解(fhq Treap)

0 条评论