无旋treap(fhq treap)

普通平衡树

/*
无旋treap(fhq treap)
treap是二叉搜索树结合二叉堆来的,
它保证树上任意节点,右节点值一定大于当前节点,儿子节点的索引一定小于父亲节点 
对于任意一个点随机赋索引,这样的树就保证了随机,从而不会被卡链
无旋treap主要引入了分裂和合并两个操作,从而可用来处理很多问题 
*/

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

const int maxn = 1e5+5;
struct node{
	int l,r;        //左右子树 
	int val,key;    //节点值和一个随机索引 
	int siz;        //子树大小 
}fhq[maxn];
int cnt,root;

int newnode(int val)    //新建一个节点 
{
	fhq[++cnt].val = val;
	fhq[cnt].key = rand();
	fhq[cnt].siz = 1;
	return cnt;
}
void update(int x)
{
	fhq[x].siz = fhq[fhq[x].l].siz + fhq[fhq[x].r].siz + 1;
} 
void split(int now,int val,int &x,int &y)   //将树按值分裂,分裂为一棵值都小于等于val和值都大于val的,根分别为x和y 
{
	if( !now ) 
	{
		x = y = 0;
		return;
	}
	if( fhq[now].val <= val )
	{
		x = now;               //这个节点及其子树都属于第一棵树 
		split(fhq[now].r,val,fhq[now].r,y);  
		//右子树中还需要相同的分解,因为里面的值可能大于val也可能小于它。
		//这里传的是引用,所以右子树在之后的递归被确定时就连上去了 
	}else
	{
		y = now;
		split(fhq[now].l,val,x,fhq[now].l);
	}
	update(now);    //最后更新一下当前节点 
}
int merge(int x,int y)  //将x与y这两棵树合并,返回合并后树的根节点,必须保证x里面所有的值都小于y里的值
{
	if( !x || !y ) return x+y;
	if( fhq[x].key > fhq[y].key )   //x的索引大于y,说明x必须在y的上方
	{
		fhq[x].r = merge(fhq[x].r,y);   //y的值又大于x的所有值,所以y一定在x的右子树上,合并x的右子树和y即可
		update(x);     //记得更新siz 
		return x;  
	}else
	{
		fhq[y].l = merge(x,fhq[y].l);
		update(y);
		return y;
	} 
} 
int x,y,z;  
void ins(int val)   //插入一个值为val的节点
//将原树按val分裂成两棵树x,y,再合并x与新节点,最后合并y 
{
	split(root,val,x,y);
	root = merge(merge(x,newnode(val)),y); 
} 
void del(int val)   //删除一个值为val的节点
//将原树按val分裂成x、z,再把x按val-1分裂成x、y.然后删去y的根节点,即合并y的左子树和右子树,再合并剩下的三棵树 
{
	split(root,val,x,z);
	split(x,val-1,x,y);
	y = merge(fhq[y].l,fhq[y].r);
	root = merge(merge(x,y),z);
}
int rank(int val)  //查询val值的排名
//将原树按val-1分裂,答案就是x树的大小,记得合并回去 
{
	split(root,val-1,x,y);
	int ans = fhq[x].siz+1;
	root = merge(x,y);
	return ans;
} 
int atrank(int x)  //查询第x大
{
	int now = root;
	while( 1 )
	{
		if( x == fhq[fhq[now].l].siz + 1 ) return fhq[now].val;
		if( fhq[fhq[now].l].siz >= x ) now = fhq[now].l;    //在左子树 
		else    //在右子树 
		{
			x -= fhq[fhq[now].l].siz + 1;
			now = fhq[now].r;
		}
	} 
} 
int pre(int val)  //查询val值的前驱 
//将原树按val-1分裂,答案就是x树上的最大值 
{
	split(root,val-1,x,y);
	int p = x;
	while( fhq[p].r ) p = fhq[p].r;
	root = merge(x,y);    //记得合并回去 
	return fhq[p].val; 
}
int nxt(int val)  //查询val值的后继
//将原树按val分裂,答案就是y树上的最小值 
{
	split(root,val,x,y);
	int p = y;
	while( fhq[p].l ) p = fhq[p].l;
	root = merge(x,y);
	return fhq[p].val;
} 

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	srand(time(NULL));
	int q;
	cin >> q;
	while( q-- )
	{
		int op,x;
		cin >> op >> x;
		if( op == 1 ) ins(x);
		else if( op == 2) del(x);
		else if( op == 3 ) cout << rank(x) << '\n';
		else if( op == 4 ) cout << atrank(x) << '\n';
		else if( op == 5 ) cout << pre(x) << '\n';
		else cout << nxt(x) << '\n';
	}
	return 0;
}

文艺平衡树

/*
无旋treap实现文艺平衡树
平衡树上维护的是按下标排序的树
翻转[l,r]区间时,将树分裂成[1,l-1]、[l,r]、[r+1,n]这三棵,对中间那棵打上懒标记即可
所以这里用的是按大小分裂 
*/

#include <iostream>
#include <cstdlib>
#include <vector>
#include <ctime>
using namespace std;

const int maxn = 1e5+5;
struct node{
	int l,r;        //左右子树 
	int id,key;    //节点值和一个随机索引 
	int siz,lazy;        //子树大小 
}fhq[maxn];
int cnt,root;

int newnode(int id)    //新建一个节点 
{
	fhq[++cnt].id = id;
	fhq[cnt].key = rand();
	fhq[cnt].siz = 1;
	return cnt;
}
void update(int x)
{
	fhq[x].siz = fhq[fhq[x].l].siz + fhq[fhq[x].r].siz + 1;
} 
void pushdown(int x)
{
	if( fhq[x].lazy )
	{
		swap(fhq[x].l,fhq[x].r);
		fhq[x].lazy = 0,fhq[fhq[x].l].lazy ^= 1,fhq[fhq[x].r].lazy ^= 1;
	}
}
void split(int now,int size,int &x,int &y)   //将树按size分裂,分裂为一棵siz等于size和剩下的一棵树,根分别为x和y 
{
	if( !now ) 
	{
		x = y = 0;
		return;
	}
	pushdown(now);
	if( fhq[fhq[now].l].siz < size )
	{
		x = now;               //这个节点及其子树都属于第一棵树 
		split(fhq[now].r,size-fhq[fhq[now].l].siz-1,fhq[now].r,y);   //记得把size减一下 
	}else
	{
		y = now;
		split(fhq[now].l,size,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 )   
	{
		pushdown(x);       //记得下传标记 
		fhq[x].r = merge(fhq[x].r,y);
		update(x);    
		return x;  
	}else
	{
		pushdown(y);
		fhq[y].l = merge(x,fhq[y].l);
		update(y);
		return y;
	} 
} 
int x,y,z;  
void ins(int id)   //插入一个编号为id的节点
//将原树按id-1大小分裂成两棵树x,y,再合并x与新节点,最后合并y 
{
	split(root,id-1,x,y);
	root = merge(merge(x,newnode(id)),y); 
} 
void reverse(int l,int r)   //翻转一个区间 
{
	split(root,l-1,x,y);    //拆成l-1和剩下的 
	split(y,r-l+1,y,z);     //将剩下拆成r-l+1和剩下的,那么y整棵树就是要操作的区间 
	fhq[y].lazy ^= 1;       //打上标记即可 
	root = merge(merge(x,y),z);
}
vector<int> ans; 
void dfs(int x)
{
	if( !x ) return;
	pushdown(x);
	dfs(fhq[x].l);
	ans.push_back(fhq[x].id);
	dfs(fhq[x].r);
} 

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	srand(time(NULL));
	int n,q;
	cin >> n >> q;
	for (int i = 1; i <= n; i++) ins(i);    //记住这里是编号 
	while( q-- )
	{
		int l,r;
		cin >> l >> r;
		reverse(l,r);
	}
	dfs(root);
	for (int i = 0; i < ans.size(); i++)
	{
		cout << ans[i];
		if( i == ans.size() - 1 ) cout << '\n';
		else cout << ' '; 
	} 
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值