#include <iostream>
#include <tuple>
using namespace std;
struct node {
node *l,*r;
int val, pri;
int cnt;
int siz;
node(int _val) {
val = _val;
siz = cnt = 1;
pri = rand();
l = r = nullptr;
}
void update() {
siz = cnt;
if (l != nullptr)
siz += l->siz;
if (r != nullptr)
siz += r->siz;
}
};
struct fhq_treap {
node *root;
pair<node *, node *> split(node *u, int val) {
//按值分裂
//分裂操作是将一个 Treap 分成 x,y 两个 Treap。其中 x 中每一个结点的值都小于等于 k,而 y 中每一个结点的值都大于等于 k。
if (u == nullptr)
return {nullptr, nullptr};
if (u->val <= val) {
//说明其左子树肯定都<=k,在x中,但是在右子树中仍然可能存在值比k小的节点(但肯定比u的值大),所以要继续递归分裂
auto tmp = split(u->r, val);
u->r = tmp.first;
//将右子树中<=k的部分拿出来作为u的右子树,这样整个u都是<=k的,剩下的都是大于于k的
u->update();
return {u, tmp.second};
}
else {
//同上
auto tmp = split(u->l, val);
u->l = tmp.second;
u->update();
return {tmp.first, u};
}
}
tuple<node *, node *, node *> splitrnk(node *u, int rnk) {
//按排名分裂,与按照值分裂类似,只是这里分成了三部分:排名<rnk的,
//排名=rnk的(并且第二个 treap即排名=rnk的 只有一个节点(不可能有多个等于的,如果有的话会增加 node 结构体中的 cnt)),以及排名>rnk的
if (u == nullptr)
return {nullptr, nullptr, nullptr};
int lsiz = u->l == nullptr ? 0 : u->l->siz;
if (rnk <= lsiz) {
// 排名和 u 相等的节点在左子树
node *l, *mid, *r;
tie(l, mid, r) = splitrnk(u->l, rnk);
u->l = r;
// 返回的第三个 treap 中的排名都大于 rnk
// u 的左子树被设成 r 后,整个 cur 中节点的排名都大于 rnk(即剥离左子树)
u->update();
return {l, mid, u};
}
else if (rnk <= lsiz + u->cnt) {
// 和 u 相等的就是当前节点
node *ltr = u->l;
node *rtr = u->r;
u->l = u->r = nullptr;
// 分裂后第二个 treap 只有一个节点,所有要把它的子树设置为空
return {ltr, u, rtr};
}
else {
// 排名和 u 相等的节点在右子树
//同上
node *l, *mid, *r;
tie(l, mid, r) = splitrnk(u->r, rnk - lsiz - u->cnt);
u->r = l;
u->update();
return {u, mid, r};
}
}
node *merge(node *u, node *v) {
//将分裂后的子树再次合并起来
//一般来说,我们合并的两个 treap 都是原来从一个 treap 中分裂出去的,所以不难满足u中所有节点的值都小于 𝑣
//即传进来的两个树的内部已经符合搜索树的性质了
// 并且 u 内所有节点的值 < v 内所有节点的值
// 所以在合并的时候需要维护堆的性质
// 这里用的是小根堆
if (u == nullptr)
return v;
if (v == nullptr)
return u;
if (u->pri < v->pri) {
// u 的 pri 比较小,u应该作为父节点
u->r = merge(u->r, v);
// 因为 v 比 u 大,所以把 v 作为 u 的右子树
u->update();
return u;
}
else {
//同理
v->l = merge(u, v->l);
v->update();
return v;
}
}
void insert(int val) {
//插入一个元素就是要找到对应值的节点的位置,并使该节点的“数量”即cnt+1(没有就创建新的)
auto tmp = split(root, val);
auto ltr = split(tmp.first, val - 1);
//找到对应的位置,注意,左子树<=val,右子树>val
if (ltr.second == nullptr)
ltr.second = new node(val);
else {
ltr.second->cnt++;
ltr.second->update();
}
root = merge(merge(ltr.first, ltr.second), tmp.second);
//所有的分裂操作不要忘记合并回来
}
void erase(int val) {
//删除一个元素就是要找到对应值的节点的位置,并使该节点的“数量”即cnt-1(刚好为1就完全删除,即设为nullptr)
auto tmp = split(root, val);
auto ltr = split(tmp.first, val - 1);
//同插入
if (ltr.second->cnt > 1) {
ltr.second->cnt--;
ltr.second->update();
}
else {
if (tmp.first == ltr.second)
//有可能整个 tmp.first 只有这个节点,所以也需要把这个点设成 nullptr 来标注已经删除
tmp.first = nullptr;
ltr.second = nullptr;
}
root = merge(merge(ltr.first, ltr.second), tmp.second);
}
int queryrank(node *u, int val) {//根据值查找排名
//排名是比这个值小的节点的数量 +1 所以我们根据 val−1分裂当前树,那么分裂后的第一个树就符合:Tree(left)<=val-1,直接返回其大小+1即可(前提为整数)
auto tmp = split(u, val - 1);
int res = (tmp.first == nullptr ? 0 : tmp.first->siz) + 1;
root = merge(tmp.first, tmp.second);
return res;
}
int queryval(node *u, int rnk) {//根据排名查找值
//直接让根据排名分裂发力
node *l, *mid, *r;
tie(l, mid, r) = splitrnk(u, rnk);
int res = mid->val;
root = merge(merge(l, mid), r);
return res;
}
int getpre(int val) {
//前驱就是比val排名小的元素里最大的
auto tmp = split(root, val - 1);
int res = queryval(tmp.first, tmp.first->siz);
root = merge(tmp.first, tmp.second);
return res;
}
int getnext(int val) {
//后驱就是比val排名大的元素里最小的
auto tmp = split(root, val);
int res = queryval(tmp.second, 1);
root = merge(tmp.first, tmp.second);
return res;
}
};
fhq_treap tr;
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
int t;
cin >> t;
while (t--) {
int opt, x;
cin >> opt >> x;
switch (opt) {
case 1:
tr.insert(x);
break;
case 2:
tr.erase(x);
break;
case 3:
cout << tr.queryrank(tr.root, x) << '\n';
break;
case 4:
cout << tr.queryval(tr.root, x) << '\n';
break;
case 5:
cout << tr.getpre(x) << '\n';
break;
case 6:
cout << tr.getnext(x) << '\n';
break;
}
}
}
/*
#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-1);
root=merge(tmp.l,merge(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() {
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;
}
*/
#include<iostream>
#include<random>
using namespace std;
mt19937 Rand(time(nullptr));
struct node{
node *l,*r;
int val,siz,pri;
node(int _val): l(nullptr),r(nullptr),val(_val),siz(1),pri(Rand()){}
void pushup(){
siz=1;
if(l!=nullptr)
siz+=l->siz;
if(r!=nullptr)
siz+=r->siz;
}
};
struct fhq_treap{
node *root;
pair<node *,node *> splitval(node *u,int val){
if(u==nullptr)
return {nullptr,nullptr};
if(u->val<=val){
auto tmp=splitval(u->r,val);
u->r=tmp.first;
u->pushup();
return {u,tmp.second};
}
else{
auto tmp=splitval(u->l,val);
u->l=tmp.second;
u->pushup();
return {tmp.first,u};
}
}
node *merge(node *u,node *v){
if(u==nullptr)
return v;
if(v==nullptr)
return u;
if(u->pri<v->pri){
u->r=merge(u->r,v);
u->pushup();
return u;
}
else{
v->l=merge(u,v->l);
v->pushup();
return v;
}
}
void insert(int val){
auto tmp=splitval(root,val-1);
auto newnode=new node(val);
root=merge(merge(tmp.first,newnode),tmp.second);
}
void erase(int val){
auto tmp=splitval(root,val-1);
auto rtr=splitval(tmp.second,val);
rtr.first=merge(rtr.first->l,rtr.first->r);
root=merge(tmp.first,merge(rtr.first,rtr.second));
}
int queryrank(int val){
auto tmp=splitval(root,val-1);
int res=tmp.first==nullptr? 1:tmp.first->siz+1;
root=merge(tmp.first,tmp.second);
return res;
}
int queryval(int rnk){
auto pos=root;
while(pos!=nullptr){
int lsiz=pos->l==nullptr? 0:pos->l->siz;
if(rnk==lsiz+1)
return pos->val;
if(rnk<lsiz+1)
pos=pos->l;
else{
rnk-=lsiz+1;
pos=pos->r;
}
}
return pos==nullptr? 0:pos->val;
}
int getpre(int val){
return queryval(queryrank(val)-1);
}
int getnext(int val){
return queryval(queryrank(val+1));
}
};
fhq_treap tr;
int main(){
cin.tie(nullptr)->sync_with_stdio(false);
int n,m;
cin>>n>>m;
for (int i = 1; i <= n; ++i) {
int tmp;
cin>>tmp;
tr.insert(tmp);
}
int last=0,ans=0;
for (int i=1;i<=m;++i){
int opt,x;
cin>>opt>>x;
if (opt==1)
tr.insert(x^last);
if (opt==2)
tr.erase(x^last);
if (opt==3) {
last=tr.queryrank(x^last);
ans^=last;
}
if (opt==4) {
last=tr.queryval(x^last);
ans^=last;
}
if (opt==5) {
last=tr.getpre(x^last);
ans^=last;
}
if (opt==6) {
last=tr.getnext(x^last);
ans^=last;
}
}
cout<<ans<<'\n';
}