浅谈fhq treap

写在前面

为什么要先写\(fhq\ treap\)呢?因为它好理解,而且好写啊(破音)!
一种依靠分裂(\(spilt\))和合并(\(merge\))操作实现的平衡树,由大神范浩强发明,所以叫\(fhq\ treap\)

优点

码量小而且核心操作是复读机(就是直接复制改一点东西然后就行)

易于理解

缺点

常数略大

操作

节点信息

左右子树编号 ,值,索引(就是\(rand()\)的值),子树大小(找排名,\(pre\)\(next\)时用)

struct node
{
	int l,r;
	int val,key;
	int size;
}fhq[N];
int cnt,root;
int nownode(int val)
{
	fhq[++cnt].val=val;
	fhq[cnt].size=1;
	fhq[cnt].key=rand();
	return cnt;
}

分裂(spilt)

分裂由两种:按值分裂和按大小分裂

这里仅介绍按值分裂,按大小分裂(可以维护区间分裂)在我做完P3391 【模板】文艺平衡树的时候会补上

按值分裂:把树拆成两棵树,拆出来的一棵树的值全部小于等于给定的值,另外一部分的值全部大于给定的值

int spilt(int now,int val,int &x,int &y) {//x和y是拆分后两树的根节点
	if(!now) x=y=0;
	else {
		if(fhq[now].val<=val) {
			x=now;
			spilt(fhq[now].r,val,fhq[now].r,y);//拆分右子树,因为右子树中可能还存在比val小的节点
		} else {
			y=now;
			spilt(fhq[now].l,val,x,fhq[now].l);//复读机
		}
		update(now);
	}
}

合并(merge)

合并就是把两棵树\(x,y\)合并成一棵树,其中x树上的所有值都小于\(y\)树上的所有值。而且新合并出来的树依旧满足\(Treap\)的性质

int merge(int x,int y) {
	if(!x||!y) return x+y;
	if(fhq[x].key>fhq[y].key) { //维护根的性质
		//根据堆的性质得到的新树x在y上面,而且根据二叉搜索树的性质y一定在x下面,所有y在x右下
		fhq[x].r=merge(fhq[x].r,y);
		update(x);
		return x;
	} else { //复读机
		fhq[y].l=merge(x,fhq[y].l);
		update(y);
		return y;
	}
}

插入

插入权值val

按值\(val\)把树分裂成\(x\)\(y\)

合并\(x\),新节点,\(y\)

只需要两行代码

解释一下为什么可以这样

按值权值\(val\)分裂得到了两棵树\(x\),\(y\)

那么x树上的所有值一定小于等于\(val\),y树上所有值一定大于\(val\),所以就可以直接合并\(x\),新节点,\(y\)

void ins(int val) {
	spilt(root,val,x,y);
	root=merge(merge(x,nownode(val)),y);//这里忘给root负值了 
}

删除

只需要四行代码

先按照\(val\)把树分裂成\(x\)\(z\)

然后按照\(val-1\)把树\(x\)分裂成\(x\)\(y\)

那么此时\(y\)树上所以值都是等于\(val\)的,我们去掉它的根节点(让\(y\)等于合并\(y\)的左子树和右子树)

void del(int val) {
	spilt(root,val,x,z);
	spilt(x,val-1,x,y);
	y=merge(fhq[y].l,fhq[y].r);
	root=merge(merge(x,y),z);
}

查询值的排名

按照\(val-1\)分裂成\(x\)\(y\)

\(x\)的大小+1就是\(val\)的排名

最后把x和y合并

int getrank(int val) {
	spilt(root,val-1,x,y);
	root=merge(x,y);
	return fhq[x].size+1;
}

其实上面是错的

int getrank(int val) {
	spilt(root,val-1,x,y);
	int ret=fhq[x].size+1;
	root=merge(x,y);//先return再合并 
	return ret;
}

查询排名的值

不写了,看代码也很好理解

int getnum(int pm) {
	int now=root;
	while(now) {
		if(fhq[fhq[now].l].size+1==pm) break;
		else if(fhq[fhq[now].l].size>=pm) now=fhq[now].l;
		else {
			pm-=fhq[fhq[now].l].size+1;
			now=fhq[now].r;
		}
	}
	return fhq[now].val;
}

前驱/后继

不知道是什么的看我上一篇博客

前驱:按照\(val-1\)分裂成\(x\)\(y\),则\(x\)里面最右的数就是\(val\)的前驱

前驱:按照\(val\)分裂成\(x\)\(y\),则\(y\)里面最左的数就是\(val\)的前驱

int pre(int val) {
	spilt(root,val-1,x,y);
	int now=x;
	while(fhq[now].r) now=fhq[now].r;
	root=merge(x,y);
	return fhq[now].val;
}
int Next(int val) {
	spilt(root,val,x,y);
	int now=y;
	while(fhq[now].l) now=fhq[now].l;
	root=merge(x,y);
	return fhq[now].val;
}

区间操作

【模板】文艺平衡树

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+2019;
struct  treap
{
	int l,r;
	int val,key;
	int size;
	int reverse;//标记 
}fhq[N];
int cnt,root;
int newnode(int val)
{
	fhq[++cnt].val=val;
	fhq[cnt].key=rand();
	fhq[cnt].size=1;
	return cnt;
}
void update(int p)
{
	fhq[p].size=fhq[fhq[p].l].size+fhq[fhq[p].r].size+1;
}
void pushdown(int p)
{
	swap(fhq[p].l,fhq[p].r);
	fhq[fhq[p].l].reverse^=1;
	fhq[fhq[p].r].reverse^=1;
	fhq[p].reverse=0;
}
void split(int now,int siz,int&x,int &y)
{ 
	if(!now) x=0,y=0;
	else 
	{
		if(fhq[now].reverse) pushdown(now);
		if(fhq[fhq[now].l].size<siz)
		{
			x=now;
			split(fhq[now].r,siz-fhq[fhq[now].l].size-1,fhq[now].r,y);
		}
		else 
		{
			y=now;
			split(fhq[now].l,siz,x,fhq[now].l);
		}
		update(now);
	}
}
int merge(int x,int y)
{
	if(!x||!y) return x+y;
	if(fhq[x].key<fhq[y].key)
	{
		if(fhq[x].reverse) pushdown(x);
		fhq[x].r=merge(fhq[x].r,y);
		update(x);
		return x;
	}
	else 
	{
		if(fhq[y].reverse) pushdown(y);
		fhq[y].l=merge(x,fhq[y].l);
		update(y);
		return y;
	}
}
void reverse(int l,int r)
{
	int x,y,z;
	split(root,l-1,x,y);
	split(y,r-l+1,y,z);
	fhq[y].reverse^=1;
	root=merge(merge(x,y),z);
}
void ldr(int x)
{
	if(!x) return ;
	if(fhq[x].reverse) pushdown(x);
	ldr(fhq[x].l);
	cout<<fhq[x].val<<" ";
	ldr(fhq[x].r);
}
int n,m;
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;++i) root=merge(root,newnode(i));
	while(m--)
	{
		int l,r;
		cin>>l>>r;
		reverse(l,r);
	}
	ldr(root);
	return 0;
}


posted @ 2019-12-19 20:58  pyyyyyy  阅读(375)  评论(0编辑  收藏  举报