原题地址:https://www.luogu.org/problemnew/show/P3369
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入xx数
- 删除xx数(若有多个相同的数,因只删除一个)
- 查询xx数的排名(排名定义为比当前数小的数的个数+1+1。若有多个相同的数,因输出最小的排名)
- 查询排名为xx的数
- 求xx的前驱(前驱定义为小于xx,且最大的数)
- 求xx的后继(后继定义为大于xx,且最小的数)
输入输出格式
输入格式:
第一行为nn,表示操作的个数,下面nn行每行有两个数optopt和xx,optopt表示操作的序号( 1 \leq opt \leq 61≤opt≤6 )
输出格式:
对于操作3,4,5,63,4,5,6每行输出一个数,表示对应答案
输入输出样例
输入样例#1:
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出样例#1:
106465
84185
492737
说明
时空限制:1000ms,128M
1.n的数据范围: n \leq 100000n≤100000
2.每个数的数据范围: [-{10}^7, {10}^7][−107,107]
来源:Tyvj1728 原名:普通平衡树
在此鸣谢
splay代码如下
#include <iostream>
#include <cstdio>
#define maxn 100005
#define INF 100000000
using namespace std;
int root;
int point;
int p; //当前还有几个节点
struct splay{
int father; //父节点
int child[2]; //左孩子,右孩子
int recy, sum; //data出现次数,左右孩子的数据总数+recy
int data; //数据
}tree[maxn];
void connect(int father, int son, int i) //使son成为father的孩子
{
tree[son].father = father;
tree[father].child[i] = son;
}
bool get(int x) //是其根节点的左孩子还是右孩子
{
int father = tree[x].father;
return tree[father].child[1] == x;
}
void update(int x) //更新sum
{
tree[x].sum = tree[tree[x].child[0]].sum + tree[tree[x].child[1]].sum + tree[x].recy;
}
void rotate(int x) //旋转
{
int y = tree[x].father;
int z = tree[y].father;
int u = get(x);
int v = get(y);
connect(y, tree[x].child[u^1], u);
connect(z, x, v);
connect(x, y, u^1);
update(y);
update(x);
}
void splay(int now, int to) //把now旋转到to位置
{
if(to == root) //
root = now;
to = tree[to].father;
while(tree[now].father != to){
int up = tree[now].father;
if(tree[up].father == to)
rotate(now);
else if(get(now) == get(up)){
rotate(up);
rotate(now);
}
else {
rotate(now);
rotate(now);
}
}
}
int crepoint(int v, int fa)
{
point ++;
tree[point].data = v;
tree[point].child[0] = tree[point].child[1] = 0;
tree[point].father = fa;
tree[point].sum = tree[point].recy = 1;
return point;
}
int insert(int v)
{
if(p == 0){ //如果没有节点了
root = crepoint(v, 0);
return root;
}
int now = root;
while(true){
tree[now].sum ++; //进过的每一个点都sum++
if(tree[now].data == v){
tree[now].recy ++;
return now;
}
else {
int t = tree[now].data < v;
if(tree[now].child[t] == 0){
int son = crepoint(v, now);
tree[now].child[t] = son;
return son;
}
now = tree[now].child[t];
}
}
return 0;
}
void push(int v)
{
int t = insert(v);
splay(t, root);
p ++;
}
void del(int x)
{
tree[x].child[0] = tree[x].child[1] = tree[x].data = tree[x].father = tree[x].recy = tree[x].sum = 0;
}
void pop(int v)
{
int now = root;
while(now != 0){
if(v == tree[now].data){
splay(now, root); //把要删除的节点旋转到根
if(tree[now].recy > 1){
tree[now].recy --;
tree[now].sum --;
}
else {
if(!tree[now].child[0]){ //如果没有左子树,直接删掉此节点,然后把右孩子设为根节点
root = tree[now].child[1];
tree[root].father = 0;
}
else {
int left = tree[now].child[0];
while(tree[left].child[1]) //找到左子树的最大值节点
left = tree[left].child[1];
splay(left, tree[now].child[0]); //把左子树最大节点旋转到根节点左孩子的位置
connect(left, tree[now].child[1], 1); //让左子树最大节点作为新的根
update(left); //连接后更新
root = left;
tree[root].father = 0;
}
del(now); //删除节点
}
p --;
return;
}
else {
int t = tree[now].data < v;
now = tree[now].child[t];
}
}
}
int rnk(int v) //返回v是第几大
{
int now = root;
int r = 0;
while(true){
int left = tree[now].child[0];
if(tree[now].data == v){
r = r + tree[left].sum + 1;
splay(now, root); //旋转后left。sum的值会变
return r;
}
else if(tree[now].data < v){
r += tree[left].sum + tree[now].recy;
now = tree[now].child[1];
}
else
now = tree[now].child[0];
}
}
int atrank(int n) //返回第n大的数
{
int now = root;
while(true){
int left = tree[now].child[0];
if(tree[left].sum >= n){
now = left;
}
else if(tree[left].sum + tree[now].recy < n){
n -= tree[left].sum + tree[now].recy;
now = tree[now].child[1];
}
else {
splay(now, root); //旋转到根
return tree[now].data;
}
}
}
int lower(int v) //前驱
{
int ans = -INF;
int now = root;
while(now){
//cout << "!! " << now << " " << tree[now].data << endl;//
if(tree[now].data < v && tree[now].data > ans)
ans = tree[now].data;
if(tree[now].data >= v)
now = tree[now].child[0];
else
now = tree[now].child[1];
}
return ans;
}
int upper(int v) //后继
{
int ans = INF;
int now = root;
while(now){
if(tree[now].data > v && tree[now].data < ans)
ans = tree[now].data;
if(tree[now].data <= v)
now = tree[now].child[1];
else
now = tree[now].child[0];
}
return ans;
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++){
int opt, x;
scanf("%d%d", &opt, &x);
//cin >> opt >> x;
if(opt == 1){
push(x);
}
else if(opt == 2){
pop(x);
}
else if(opt == 3){
printf("%d\n", rnk(x));
//cout << rnk(x) << endl;
}
else if(opt == 4){
printf("%d\n", atrank(x));
}
else if(opt == 5){
printf("%d\n", lower(x));
}
else {
printf("%d\n", upper(x));
}
}
return 0;
}
7.12更新
常数更小更容易写的LeafyTree代码如下:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ratio 4
using namespace std;
typedef long long ll;
inline int read(){
int res = 0, w = 0; char ch = 0;
while(!isdigit(ch)){
w |= ch == '-', ch = getchar();
}
while(isdigit(ch)){
res = (res << 3) + (res << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -res : res;
}
const int N = 100005;
int siz[N << 2], data[N << 2], lch[N << 2], rch[N << 2];
int cnt, root;
void merge(int l, int r)
{
++cnt;
siz[cnt] = siz[l] + siz[r];
data[cnt] = data[r];
lch[cnt] = l;
rch[cnt] = r;
}
void rotate(int k, bool flg)
{
if(flg){
merge(rch[lch[k]], rch[k]);
lch[k] = lch[lch[k]];
rch[k] = cnt;
}
else {
merge(lch[k], lch[rch[k]]);
lch[k] = cnt;
rch[k] = rch[rch[k]];
}
}
void maintain(int k)
{
if(siz[lch[k]] > siz[rch[k]] * ratio)
rotate(k, 1);
else if(siz[rch[k]] > siz[lch[k]] * ratio)
rotate(k, 0);
}
int newnode(int v)
{
++cnt;
siz[cnt] = 1;
data[cnt] = v;
return cnt;
}
void pushup(int k)
{
if(siz[lch[k]] == 0)
return;
siz[k] = siz[lch[k]] + siz[rch[k]];
data[k] = data[rch[k]];
}
void insert(int k, int x)
{
if(siz[k] == 1){
lch[k] = newnode(min(data[k], x));
rch[k] = newnode(max(data[k], x));
pushup(k);
return;
}
insert(x <= data[lch[k]] ? lch[k] : rch[k], x);
maintain(k);
pushup(k);
}
void cpynode(int a, int b)
{
siz[a] = siz[b];
data[a] = data[b];
lch[a] = lch[b];
rch[a] = rch[b];
}
void del(int k, int fa, int x)
{
if(siz[k] == 1){
cpynode(fa, k == lch[fa] ? rch[fa] : lch[fa]);
return;
}
del(x <= data[lch[k]] ? lch[k] : rch[k], k, x);
maintain(k);
pushup(k);
}
int rnk(int k, int x)
{
if(siz[k] == 1)
return 1;
if(x <= data[lch[k]])
return rnk(lch[k], x);
else
return rnk(rch[k], x) + siz[lch[k]];
}
int atrank(int k, int x)
{
if(siz[k] == x)
return data[k];
if(x > siz[lch[k]])
return atrank(rch[k], x - siz[lch[k]]);
else
return atrank(lch[k], x);
}
int lower(int x)
{
return atrank(root, rnk(root, x) - 1);
}
int upper(int x)
{
return atrank(root, rnk(root, x + 1));
}
int main()
{
int n;
scanf("%d", &n);
root = newnode(INF);
for(int i = 1; i <= n; i ++){
int opt, x;
scanf("%d%d", &opt, &x);
if(opt == 1)
insert(root, x);
else if(opt == 2)
del(root, 0, x);
else if(opt == 3)
printf("%d\n", rnk(root, x));
else if(opt == 4)
printf("%d\n", atrank(root, x));
else if(opt == 5)
printf("%d\n", lower(x));
else if(opt == 6)
printf("%d\n", upper(x));
}
return 0;
}