#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 1;
struct Node{
int s[2];//左右儿子,s[0]为左儿子,s[1]为右儿子
int p;//父亲
int v;//节点权值
int cnt;//权值出现次数
int size;//子树大小
void init(int p1, int v1) {
p = p1, v = v1;
cnt = size = 1;
}
}tr[N];
int n;
int root;//根节点编号
int idx;//节点个数
void pushup(int x) {
tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + tr[x].cnt;
}
void rotate(int x) {
int y = tr[x].p, z = tr[y].p;//获取父亲节点
int k = tr[y].s[1] == x;//判断是左儿子还是右儿子
tr[y].s[k] = tr[x].s[k ^ 1];//把 x 的右儿子给 y,作为它的左儿子
tr[tr[x].s[k ^ 1]].p = y;// x 的右儿子的父亲改为 y
tr[x].s[k ^ 1] = y;
tr[y].p = x;
tr[z].s[tr[z].s[1] == y] = x;
tr[x].p = z;
pushup(y), pushup(x);
}
void splay(int x, int k) {//将 x 节点转到目标节点 k 的下面作儿子
while(tr[x].p != k) {
int y = tr[x].p, z = tr[y].p;
if(z != k) //折线形转底节点 x,直线型转中节点 y
(tr[y].s[0] == x) ^ (tr[z].s[1] == y) ? rotate(x) : rotate(y);
rotate(x);
}
if(k == 0) root = x;//转到根节点做标记
}
void find(int v) {//找到 v 并转到根节点
int x = root; //从根节点进入
while(tr[x].s[v > tr[x].v] && v != tr[x].v) //当 x 的儿子节点为空或找到了 v,则退出
x = tr[x].s[v > tr[x].v];
splay(x, 0);
}
int get_pre(int v) {//前驱
find(v);
int x = root;
if(tr[x].v < v) return x;
x = tr[x].s[0]; //往左子树走
while(tr[x].s[1]) x = tr[x].s[1]; //左子树中最大的点在其右子树上
return x;
}
int get_suc(int v) {//后继
find(v);
int x = root;
if(tr[x].v > v) return x;
x = tr[x].s[1];//往右子树走
while(tr[x].s[0]) x = tr[x].s[0]; //右子树上最小的点在其左子树上
return x;
}
void del(int v) {//删除
int pre = get_pre(v);
int suc = get_suc(v);
splay(pre, 0), splay(suc, pre);
int del = tr[suc].s[0];
if(tr[del].cnt > 1) { //如果 v 有多个,个数 -1
tr[del].cnt --;
splay(del, 0); //更新受影响的子树
}
else { //否则直接清空
tr[suc].s[0] = 0;
splay(suc, 0); //更新受影响的子树
}
}
int get_rank(int v) {//排名
find(v);
return tr[tr[root].s[0]].size;
}
int get_val(int k) {//数值
int x = root;
while(1) {
int y = tr[x].s[0]; //取 x 的左儿子
if(tr[y].size + tr[x].cnt < k) { //如果左子树加根的大小比 k 小,则说明要查询的数在右子树
k -= tr[y].size + tr[x].cnt; //减去左子树大小
x = tr[x].s[1]; //往右子树走
}
else {
if(tr[y].size >= k) x = tr[x].s[0]; //若左子树大小比 k 大,说明要查询的数在左子树
else break; //走不了,说明找到了,退出
}
}
splay(x, 0);
return tr[x].v;
}
void insert(int v) {//插入
int x = root, p = 0;// p 为记录当前节点的父节点
while(x && tr[x].v != v) {//走到空节点或当前节点的值等于插入值则退出
p = x;
x = tr[x].s[v > tr[x].v]; //判断往哪个子树走
}
if(x) tr[x].cnt ++; //若节点不为空,说明之前插入过此值,个数 +1
else { //否则新建一个点
x = ++ idx;
tr[p].s[v > tr[p].v] = x;
tr[x].init(p, v);
}
splay(x, 0);
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
insert(-1e18), insert(1e18);
cin >> n;
while(n --) {
int op, x;
cin >> op >> x;
if(op == 1) insert(x);
if(op == 2) del(x);
if(op == 3) cout << get_rank(x) << '\n';
if(op == 4) cout << get_val(x + 1) << '\n';
if(op == 5) cout << tr[get_pre(x)].v << '\n';
if(op == 6) cout << tr[get_suc(x)].v << '\n';
}
return 0;
}
对于以下这份数据
5
1 24
1 25
1 20
3 23
3 23
应输出
2
2
实际输出
2
1