P6136 【模板】普通平衡树(数据加强版) 非旋转treap算法指针版

2022/3/27 20:22:58

本文主要是介绍P6136 【模板】普通平衡树(数据加强版) 非旋转treap算法指针版,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

P6136 【模板】普通平衡树(数据加强版)

//非旋转的Treap树 
#include<bits/stdc++.h>
using namespace std;
const int INF=0x7fffffff;
struct Node *nil; // 自定义的空指针,防止翻车(RE)
struct Node {
	Node *ch[2]; // 结点的左右孩子。为什么不分开写成lc,rc呢?往后就知道了
	int v, s, c; // v表示该结点的值,s表示以该结点为根的子树大小,c表示权值v的结点数量(合并了相同权值的结点),即v的副本数量
	int r; // Treap专有的随机数,是大根堆的关键字
	void maintain() { // 维护当前结点的信息
		s = ch[0]->s + ch[1]->s + c;
	}
	Node(int v) : v(v), c(1), s(1), r(rand()) { ch[0] = ch[1] = nil; } // 新建结点
} *root;
void init() {
	srand(0); // 随机数初始化
	nil = new Node(0); // 空指针初始化
	root = nil->ch[0] = nil->ch[1] = nil; // 左右孩子指向自身
	nil->s = nil->c = 0; // 防止空指针影响后面的操作
}
void split(Node *o, int v, Node* &l, Node* &r) {
	if (o == nil) { l = r = nil; return; } // 到了空结点两边附上空后结束
	if (o->v < v) { l = o; split(o->ch[1], v, l->ch[1], r); l->maintain(); } // 第一种情况。注意分裂后要及时维护信息
	else { r = o; split(o->ch[0], v, l, r->ch[0]); r->maintain(); } // 第二种情况。同上
}
Node* merge(Node *a, Node *b) {
	if (a == nil) return b; // 如果a空返回b
	if (b == nil) return a; // 如果b空返回a
	if (a->r > b->r) { a->ch[1] = merge(a->ch[1], b); a->maintain(); return a; } // 比较两者的随机关键字,大的先合并
	else { b->ch[0] = merge(a, b->ch[0]); b->maintain(); return b; }
}
void insert(Node* &rt, int v) {
	Node *l, *r;
	split(rt, v, l, r);
	rt = merge(merge(l, new Node(v)), r);
}
void remove(Node* &rt, int v) {
	Node *l, *mid, *r;
	split(rt, v, l, r);
	split(r, v+1, mid, r);
	rt = merge(merge(l, merge(mid->ch[0], mid->ch[1])), r);
	delete mid;
}
//v的排名 
int get_rank(Node* o, int v) {
	/*
	        10
	        /\
		   4
		   \
		   10
		   /\
		  7
		 /\
		.. 10 	 
	有错 
	if (v < o->v) return get_rank(o->ch[0], v); // v在左子树中
	if (o->ch[0]&&o->ch[0]->v==v) return get_rank(o->ch[0],v);
	if (o->v== v) return o->ch[0]->s + 1; // 找到v
	return get_rank(o->ch[1], v) + o->ch[0]->s + o->c;
	// v在右子树中,此时要将左子树中的所有元素以及根的元素数统计起来
	*/
	/*
	int ans=0;
	while(o!=nil)
	{
		if (o->v>=v)
		{
			o=o->ch[0];	
		}
		else
		{
			ans+=o->ch[0]->s+1;
			o=o->ch[1];
		}
	}
	return ans+1;	
	*/
	if (o==nil) return 1;
	if (o->v>=v) return get_rank(o->ch[0],v);
	else return o->ch[0]->s+1+get_rank(o->ch[1],v);
}
//第k小数 
int get_val(Node* o, int k) {
	if (k <= o->ch[0]->s) return get_val(o->ch[0], k); // 排名为k的数在左子树中
	else if (k == o->ch[0]->s + o->c) return o->v; // 为o
	else return get_val(o->ch[1], k - o->ch[0]->s - o->c); // 在右子树中
}
int get_pre(Node *o, int v) { // 查前驱
	if (o == nil) return -INF; // 找到空结点返回无穷小目的是不干扰其它解
	if (o->v >= v) return get_pre(o->ch[0], v); // v大于当前结点o的值,向左走找小一点的值
	return max(o->v, get_pre(o->ch[1], v)); // 否则向右走找更大的值并与当前值比较
	/*
	有点慢 
	if (o == nil) return -INF; // 找到空结点返回无穷小目的是不干扰其它解
	return get_val(o,get_rank(o,v)-1);	
	*/
}
int get_next(Node *o, int v) { // 查后继,与上面完全相反
	/*
	if (o == nil) return INF;
	if (o->v <= v) return get_next(o->ch[1], v);
	return min(o->v, get_next(o->ch[0], v));
	*/
	return get_val(o,get_rank(o,v+1));//有点慢 
}
int main()
{
	int n,m,op,x,last=0,ans=0;
	init();
	scanf("%d %d",&n,&m);
	while(n--)
	{
		scanf("%d",&x);
		insert(root,x);
	}
	while(m--)
	{
		scanf("%d %d",&op,&x);
		x=last^x;
		if (op==1) 
		   insert(root,x);
		else if(op==2) 
		   remove(root,x);
		else if(op==3) 
		{
			last=get_rank(root,x);
			ans^=last;
		}
		else if(op==4)
		{
			last=get_val(root,x);
			ans^=last;
		} 
		else if(op==5)
		{
			last=get_pre(root,x);
			ans^=last;
		} 
		else if(op==6) 
		{
			last=get_next(root,x);
			ans^=last;
		}
	}
	cout<<ans<<endl;
}

  



这篇关于P6136 【模板】普通平衡树(数据加强版) 非旋转treap算法指针版的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程