无旋Treap(非指针)实现(文艺平衡树,也包括其余功能)

/*
#include<bits/stdc++.h>

namespace fastIO{
	template<typename T> inline void input(T& x){
		T s=0,f=1;
		char ch=getchar();
		for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
		for(;isdigit(ch);ch=getchar()) s=(s<<3)+(s<<1)+(ch^'0');
		x=s*f;
	}
	template<typename T> inline void print(T x){
		if(x<0){
			putchar('-');
			x=-x;
		}
		if(x>9) print(x/10);
		putchar(x%10+'0');
	}
	template<typename T,typename... Args> inline void input(T& x,Args&... args){
		input(x);
		input(args...);
	}
	template<typename T> inline void print(T x,char ch){
		print(x);
		putchar(ch);
	}
}
using namespace fastIO;

const int N=1e6+5;
namespace Treap{
	//树堆的性质:val(l[u])<val(u)<val(r[u])
	struct Pair{
		int l,r;
		Pair(int l_=0,int r_=0): l(l_),r(r_){}
	};
	struct fhq_treap{
		int val,l,r,siz,pri,lazy;//值,第u个节点的左右孩子),u为根节点的子树的大小,u的优先级,懒标记
	};
	fhq_treap tree[N];
	const int INF=0x3f3f3f3f;
	int seed=1;
	int tot=0;//节点总数
	int root=0;//根节点

	inline int rand1(){
		return seed*=19260817;
	}
	inline void pushup(int u){
		tree[u].siz=tree[tree[u].l].siz+tree[tree[u].r].siz+1;
	}
	//区间操作代码
	inline void pushdown(int u){
		if(!tree[u].lazy) return ;
		std::swap(tree[u].l,tree[u].r);//颠倒区间
		tree[tree[u].l].lazy^=1;
		tree[tree[u].r].lazy^=1;//异或很容易实现“翻转2次=没有翻转”
		tree[u].lazy=0;
	}
	Pair splitrank(int u,int k){
		if(!u) return {0,0};
		pushdown(u);
		if(tree[tree[u].l].siz+1<=k){
			auto tmp=splitrank(tree[u].r,k-tree[tree[u].l].siz-1);//原理类似于queryrank
			tree[u].r=tmp.l;
			pushup(u);
			return {u,tmp.r};
		}
		else{
			auto tmp=splitrank(tree[u].l,k);
			tree[u].l=tmp.r;
			pushup(u);
			return {tmp.l,u};
		}
	}
	void splitrank(int u,int k,int& l,int& r){//等价写法
		if(!u){
			l=r=0;
			return ;
		}
		pushdown(u);
		if(tree[tree[u].l].siz+1<=k){
			l=u;//可以这么理解将左子树分配给了u(以下同理,至于为什么这么干可以去看splitval)
			splitrank(tree[u].r,k-tree[tree[u].l].siz-1,tree[u].r,r);
		}
		else{
			r=u;
			splitrank(tree[u].l,k,l,tree[u].l);
		}
	}
	int mergerank(int u,int v){
		if(!u || !v) return u+v;
		if(tree[u].pri<tree[v].pri){
			pushdown(u);
			tree[u].r=mergerank(tree[u].r,v);
			pushup(u);
			return u;
		}
		else{
			pushdown(v);
			tree[v].l=mergerank(u,tree[v].l);
			pushup(v);
			return v;
		}
	}
	void printree(int u){
		//对于颠倒对应区间[l,r]的操作,只需要分裂出[1,l),[l,r],(r,n]三个区间,在进行颠倒操作即可(懒标记)
		//颠倒后仍是中序遍历
		pushdown(u);
		if(tree[u].l) printree(tree[u].l);
		print(tree[u].val,' ');
		if(tree[u].r) printree(tree[u].r);
	}
	//
	Pair splitval(int u,int k){//根节点为u,按k分裂,返回分裂后两棵子树的根
		//分裂操作是将一个 Treap 分成 x,y 两个 Treap。其中 x 中每一个结点的值都小于于 k,而 y 中每一个结点的值都大于等于 k。
		if(!u) return {0,0};//空树
		if(tree[u].val<=k){
			//说明其左子树肯定都<=k,在x中,但是在右子树中仍然可能存在值比k小的节点(但肯定比u的值大),所以要继续递归分裂
			auto tmp=splitval(tree[u].r,k);
			tree[u].r=tmp.l;//将右子树中<=k的部分拿出来作为u的右子树,这样整个u都是<=k的,剩下的都是大于于k的
			pushup(u);
			return {u,tmp.r};
		}
		else{
			//同上
			auto tmp=splitval(tree[u].l,k);
			tree[u].l=tmp.r;
			pushup(u);
			return {tmp.l,u};
		}
	}
	void splitval(int u,int k,int& l,int& r){//等价写法(同splitrank)
		if(!u){
			l=r=0;
			return ;
		}
		if(tree[u].val<=k){
			l=u;
			splitval(tree[u].r,k,tree[u].r,r);
		}
		else{
			r=u;
			splitval(tree[u].l,k,l,tree[u].l);
		}
	}
	int mergeval(int u,int v){//合并u、v两棵子树,返回合并后子树的根
		//合并是将 x,y 两个 Treap 合并为一个 Treap(因为通常是从同一棵Treap分裂出来的,所以x 中的每一个结点的值都小于等于 y 中每一个结点的值)
		if(!u || !v) return u+v;//只要有一棵为空,就直接返回另一棵
		if(tree[u].pri<tree[v].pri){//需要满足堆的性质,即根节点的优先级是最小的,所以优先级小的作为合并后的根节点
			tree[u].r=mergeval(tree[u].r,v); //将右子树和另一棵树合并,作为右子树
			pushup(u);
			return u;
		}
		else{
			//反之同理
			tree[v].l=mergeval(u,tree[v].l);
			pushup(v);
			return v;
		}
	}
	void insert(int k){//插入一个值为 k 的结点
		//先将申请的一个新的结点作为一棵树 y,并将原来的树分裂成 x,z 两棵树,然后依次合并 x,y,z,就完成了。
		tree[++tot].val=k;
		tree[tot].pri=rand1();
		tree[tot].siz=1;
		tree[tot].l=tree[tot].r=0;
		//申请一个节点,作为一棵树,其大小为 1
		auto tmp=splitval(root,k-1);//以 k 为关键值分裂
		root=mergeval(tmp.l,mergeval(tot,tmp.r));//依次合并,更新根
	}
	void erase(int k){//删除值为k的节点
		//其实很简单,即将整棵树按值分为小于k,等于k与大于k三部分,直接合并小于k与大于k的部分即可
		auto x=splitval(root,k-1);//分为<=和>两部分
		auto y=splitval(x.r,k);//提取=(y.r的根节点)
		y.l=mergeval(tree[y.l].l,tree[y.l].r);//直接合并左右子树,就能实现删除操作(不用担心会额外删除,多的在左子树里)
		root=mergeval(x.l,mergeval(y.l,y.r));//更新根节点
	}
	int queryrank(int k){//根据值查询排名
		auto tmp=splitval(root,k-1);
		int res=tree[tmp.l].siz+1;//小于k的值的数量+1
		root=mergeval(tmp.l,tmp.r);//合并回去
		return res;
	}
	int queryval(int k){//根据排名查询值
		int pos=root;//初始化排名
		while(pos){
			if(k==tree[tree[pos].l].siz+1) return tree[pos].val;
			//若其左子树大小+1刚好为排名,则说明其就是要找的点(左子树存的是<=的,等于的在根节点)
			if(k<tree[tree[pos].l].siz+1) pos=tree[pos].l;//进入左子树寻找
			else{//不在左子树也不在根节点,就只能去右子树了
				k-=tree[tree[pos].l].siz+1;//将k减去左子树大小+1(1为根节点大小)(自己模拟一下很容易理解)
				pos=tree[pos].r;//进入右子树寻找
			}
		}
		return INF;//以防万一
	}
	int getpre(int k){//查找值k的前驱
		//什么叫“小于 k,且最大的数”?不就是前面的那个数吗,所以直接查找排名比 k 的排名少一的数。
		return queryval(queryrank(k)-1);
	}
	int getnext(int k){//查找k的后驱
		//值相同的结点可能不止一个,所以不能找后面的那个数了,但是可以将值加一,查询它的排名,并找到对应的值。
		return queryval(queryrank(k+1));
	}
}
using namespace Treap;

int n,m;

int main(){
	input(n,m);
	for(int i=1;i<=n;++i){
		insert(i);
	}
	while(m--){
		int l,r;
		input(l,r);
		auto x=splitrank(root,r);
		auto y=splitrank(x.l,l-1);
		tree[y.r].lazy^=1;
		root=mergerank(mergerank(y.l,y.r),x.r);
	}

	printree(root);
	return 0;
}
*/
#include<bits/stdc++.h>

using namespace std;

const int N=1100010;
struct Pair{
	int l,r;
	Pair(int l_=0,int r_=0): l(l_),r(r_){}
};
struct node{
	int l,r,pri,val,siz;
};
node tree[N];
int tot,root;
int n,m;

inline void pushup(int u){
	tree[u].siz=tree[tree[u].l].siz+tree[tree[u].r].siz+1;
}
Pair splitval(int u,int k){
	if(!u) return {0,0};
	if(tree[u].val<=k){
		auto tmp=splitval(tree[u].r,k);
		tree[u].r=tmp.l;
		pushup(u);
		return {u,tmp.r};
	}
	else{
		auto tmp=splitval(tree[u].l,k);
		tree[u].l=tmp.r;
		pushup(u);
		return {tmp.l,u};
	}
}
int merge(int u,int v){
	if(!u || !v) return u+v;
	if(tree[u].pri<tree[v].pri){
		tree[u].r=merge(tree[u].r,v);
		pushup(u);
		return u;
	}
	else{
		tree[v].l=merge(u,tree[v].l);
		pushup(v);
		return v;
	}
}
void insert(int k){
	tree[++tot].val=k;
	tree[tot].siz=1;
	tree[tot].pri=rand();
	tree[tot].l=tree[tot].r=0;
	auto tmp=splitval(root,k);
	root=merge(merge(tmp.l,tot),tmp.r);
}
void erase(int k){
	auto x=splitval(root,k-1);
	auto y=splitval(x.r,k);
	y.l=merge(tree[y.l].l,tree[y.l].r);
	root=merge(x.l,merge(y.l,y.r));
}
int queryrank(int k){
	auto tmp=splitval(root,k-1);
	int res=tree[tmp.l].siz+1;
	root=merge(tmp.l,tmp.r);
	return res;
}
int queryval(int k){
	int pos=root;
	while(pos){
		if(k==tree[tree[pos].l].siz+1) return tree[pos].val;
		if(k<tree[tree[pos].l].siz+1) pos=tree[pos].l;
		else{
			k-=tree[tree[pos].l].siz+1;
			pos=tree[pos].r;
		}
	}
	return 0x3f3f3f3f;
}
int getpre(int k){
	return queryval(queryrank(k)-1);
}
int getnext(int k){
	return queryval(queryrank(k+1));
}

int main(){
//	freopen("P6136_2.in","r",stdin);
	cin.tie(nullptr)->sync_with_stdio(false);
	cin>>n>>m;
	for (int i = 1; i <= n; ++i) {
		int tmp;
		cin>>tmp;
		insert(tmp);
	}
	
	int last=0,ans=0;
	for (int i = 1; i <= m; ++i) {
		int opt,x;
		cin>>opt>>x;
		if (opt == 1) insert(x^last);
		if (opt == 2) erase(x^last);
		if (opt == 3) {
			last = queryrank(x^last);
			ans ^= last;
		}
		if (opt == 4) {
			last = queryval(x^last);
			ans ^= last;
		}
		if (opt == 5) {
			last = getpre(x^last);
			ans ^= last;
		}
		if (opt == 6) {
			last = getnext(x^last);
			ans ^= last;
		}
//		cout<<last<<'\n';
	}
	
	cout<<ans<<'\n';
	return 0;
}
posted @ 2025-09-28 20:24  _CENSORED  阅读(5)  评论(0)    收藏  举报