闲话
P3369 【模板】普通平衡树
会 TLE 一个点,过不了。
但是可以过P5076 【深基16.例7】普通二叉树(简化版),虽然只是道绿,但是可以水一下。
二叉搜索树
首先它得是一颗二叉树。
建立的过程:一个值
x
x
x 加进来,大于当前值
n
o
w
now
now,就往右子树走,小于当前值
n
o
w
now
now,就往左子树走,否则
x
x
x 就是当前的点,点的数量加一。
这样建树可以满足一个性质:
- 一个点的左子树中所有的点都小于它,右子树中的所有点都大于它,所以一颗二叉搜索树的中序遍历是升序的。
把一颗二叉搜索树建立成这样:
struct node{
int l,r,cnt,ans,flag;
int lans;
}f[N];
l , r l,r l,r 是左右儿子编号, c n t cnt cnt 是当前元素数量, a n s ans ans 是元素值, f l a g flag flag 表示这个点存不存在, l a n s lans lans 是左子树大小。
插入
同上。
void add(int p,int x){
if(!f[p].flag){
f[++cnt]=node{0,0,1,x,1,0};
return;
}
if(x>f[p].ans){
if(f[f[p].r].flag){
add(f[p].r,x);
}
else{
f[++cnt]=node{0,0,1,x,1,0};
f[p].r=cnt;
}
}
else if(x<f[p].ans){
f[p].lans++;
if(f[f[p].l].flag){
add(f[p].l,x);
}
else{
f[++cnt]=node{0,0,1,x,1,0};
f[p].l=cnt;
}
}
else f[p].cnt++;
}
删除
如果当前的值有多个,即
c
n
t
>
1
cnt>1
cnt>1,将
c
n
t
cnt
cnt 减一即可。
如果当前点只有一个儿子,那么把这个儿子提上来即可。
重点放在如果有两个儿子的情况。

红色的点是删除的点。
现在它空了,需要找一个点替上。
可以考虑直接上提左子树,再将右子树接在左子树最右端的点上,二叉搜索树性质不变。
但是这样太麻烦了,考虑不移动原来树上的点。
可以考虑在删除点的位置加一个点,因为要维护二叉搜索树中序遍历升序的性质,这个点的值可以使左子树中的最大值或者右子树中的最小值。
现在令将点
x
x
x 移动到空缺的位置,由于这里有一个
x
x
x,那么原来的位置需要删除。
如果把原来的点
x
x
x 中
f
[
x
]
.
c
n
t
f[x].cnt
f[x].cnt 个元素全部移动过来,原来的位置就需要删除
f
[
x
]
.
c
n
t
f[x].cnt
f[x].cnt 次,还是费时间。
考虑只用点
x
x
x 占个位置,这样只用删除一次原点。
int getmin(int p){
if(f[f[p].l].flag)return getmin(f[p].l);
return p;
}
bool remove(int p,int x){
if(!f[p].flag)return 0;
if(f[p].ans==x){
if(f[p].cnt>1)f[p].cnt--;//如果有多个,直接删
else{
if(f[f[p].l].flag&&f[f[p].r].flag){//这里选择的是提右子树中的最小值
int q=getmin(f[p].r);
f[p].ans=f[q].ans;
remove(f[p].r,f[q].ans);
}
else if(f[f[p].l].flag)f[p]=f[f[p].l];//有任意一个,直接提上
else if(f[f[p].r].flag)f[p]=f[f[p].r];
else f[p].flag=0;//叶子结点直接删
}
return 1;
}
else if(x>f[p].ans){
return remove(f[p].r,x);
}
else{
bool glaf=remove(f[p].l,x);//如果删除了点才将 lans 减一
if(glaf)f[p].lans--;
return glaf;
}
}
前驱
x
x
x 的前驱:序列中小于
x
x
x 的元素中的最大值。
二叉搜索树的性质:左子树中的值都小于当前值。
所以遇到大于等于查询值
x
x
x 的就往左子树走。
如果遇到
f
[
p
]
.
a
n
s
<
x
f[p].ans<x
f[p].ans<x,那么记录
p
p
p,然后往右子树走。(因为前驱需要尽可能大)
void pre(int p,int x){
if(!f[p].flag)return;
if(f[p].ans<x){
Pre=f[p].ans;
pre(f[p].r,x);
}
else{
pre(f[p].l,x);
}
}
后继
x
x
x 的后继:序列中大于
x
x
x 的元素中的最小值。
二叉搜索树的性质:右子树中的值都大于当前值。
所以遇到小于等于查询值
x
x
x 的就往右子树走。
如果遇到
f
[
p
]
.
a
n
s
>
x
f[p].ans>x
f[p].ans>x,那么记录
p
p
p,然后往左子树走。(因为后继需要尽可能小)
void suc(int p,int x){
if(!f[p].flag)return;
if(f[p].ans>x){
Suc=f[p].ans;
suc(f[p].l,x);
}
else{
suc(f[p].r,x);
}
}
查排名
x
x
x 的排名:将序列升序排序后,第一个值为
x
x
x 的数的位置。
这时候
l
a
n
s
lans
lans 就有用了。
l
a
n
s
lans
lans 表示左子树的大小,也就是表示序列中有多少元素小于
f
[
p
]
.
a
n
s
f[p].ans
f[p].ans 。
找到一个点
p
p
p 时,若
f
[
p
]
.
a
n
s
>
x
f[p].ans>x
f[p].ans>x,对于排名无贡献,往左子树走。
若
f
[
p
]
.
a
n
s
=
x
f[p].ans=x
f[p].ans=x,此时的贡献就是
f
[
p
]
.
l
a
n
s
+
1
f[p].lans+1
f[p].lans+1(因为是排名,所以要加一)
若
f
[
p
]
.
a
n
s
<
x
f[p].ans<x
f[p].ans<x,说明当前点
p
p
p 以及
p
p
p 的左子树中的所有点都比
x
x
x 小,对排名的贡献为
f
[
p
]
.
l
a
n
s
+
f
[
p
]
.
l
a
n
s
f[p].lans+f[p].lans
f[p].lans+f[p].lans。
图:

int ranking(int p,int x){
if(!f[p].flag){
return 1;
}
if(f[p].ans==x){
return f[p].lans+1;
}
else if(x>f[p].ans){
return f[p].lans+f[p].cnt+ranking(f[p].r,x);
}
else{
return ranking(f[p].l,x);
}
}
根据排名查询数
查询排名为
x
x
x 的数,就是查询大于等于
x
−
1
x-1
x−1 个数的元素。
利用
l
a
n
s
lans
lans,把排名查询反过来就行了。
int query(int p,int x){
if(f[p].lans+f[p].cnt<=x){
return query(f[p].r,x-f[p].lans-f[p].cnt);
}
else if(f[p].lans<=x){
return f[p].ans;
}
return query(f[p].l,x);
}
P3369 【模板】普通平衡树
会 TLE,只有 91 分。
#include<bits/stdc++.h>
#define int long long
#define endl putchar('\n')
#define psp putchar(' ')
using namespace std;
const int N=1e6+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
int m;
int cnt;
struct node{
int l,r,cnt,ans,flag;
int lans;
}f[N];
int Pre;
int Suc;
int now;
void add(int p,int x){
if(!f[p].flag){
f[++cnt]=node{0,0,1,x,1,0};
return;
}
if(x>f[p].ans){
if(f[f[p].r].flag){
add(f[p].r,x);
}
else{
f[++cnt]=node{0,0,1,x,1,0};
f[p].r=cnt;
}
}
else if(x<f[p].ans){
f[p].lans++;
if(f[f[p].l].flag){
add(f[p].l,x);
}
else{
f[++cnt]=node{0,0,1,x,1,0};
f[p].l=cnt;
}
}
else f[p].cnt++;
}
int getmin(int p){
if(f[f[p].l].flag)return getmin(f[p].l);
return p;
}
bool remove(int p,int x){
if(!f[p].flag)return 0;
if(f[p].ans==x){
if(f[p].cnt>1)f[p].cnt--;//如果有多个,直接删
else{
if(f[f[p].l].flag&&f[f[p].r].flag){//这里选择的是提右子树中的最小值
int q=getmin(f[p].r);
f[p].ans=f[q].ans;
remove(f[p].r,f[q].ans);
}
else if(f[f[p].l].flag)f[p]=f[f[p].l];//有任意一个,直接提上
else if(f[f[p].r].flag)f[p]=f[f[p].r];
else f[p].flag=0;//叶子结点直接删
}
return 1;
}
else if(x>f[p].ans){
return remove(f[p].r,x);
}
else{
bool glaf=remove(f[p].l,x);//如果删除了点才将 lans 减一
if(glaf)f[p].lans--;
return glaf;
}
}
int ranking(int p,int x){
if(!f[p].flag){
return 1;
}
if(f[p].ans==x){
return f[p].lans+1;
}
else if(x>f[p].ans){
return f[p].lans+f[p].cnt+ranking(f[p].r,x);
}
else{
return ranking(f[p].l,x);
}
}
int query(int p,int x){
if(f[p].lans+f[p].cnt<=x){
return query(f[p].r,x-f[p].lans-f[p].cnt);
}
else if(f[p].lans<=x){
return f[p].ans;
}
return query(f[p].l,x);
}
void pre(int p,int x){
if(!f[p].flag)return;
if(f[p].ans<x){
Pre=f[p].ans;
pre(f[p].r,x);
}
else{
pre(f[p].l,x);
}
}
void suc(int p,int x){
if(!f[p].flag)return;
if(f[p].ans>x){
Suc=f[p].ans;
suc(f[p].l,x);
}
else{
suc(f[p].r,x);
}
}
signed main(){
m=read();
while(m--){
int op=read(),x=read();
if(op==1){
add(1,x);
now++;
}
else if(op==2){
if(remove(1,x))now--;
if(now==0)cnt=0;
}
else if(op==3){
print(ranking(1,x)),endl;
}
else if(op==4){
print(query(1,x-1)),endl;
}
else if(op==5){
pre(1,x);
print(Pre),endl;
}
else{
suc(1,x);
print(Suc),endl;
}
}
}
P5076 【深基16.例7】普通二叉树(简化版)
#include<bits/stdc++.h>
#define int long long
#define endl putchar('\n')
#define psp putchar(' ')
using namespace std;
const int N=1e6+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
int m;
int cnt;
struct node{
int l,r,cnt,ans,flag;
int lans;
}f[N];
int Pre;
int Suc;
int now;
void add(int p,int x){
if(!f[p].flag){
f[++cnt]=node{0,0,1,x,1,0};
return;
}
if(x>f[p].ans){
if(f[f[p].r].flag){
add(f[p].r,x);
}
else{
f[++cnt]=node{0,0,1,x,1,0};
f[p].r=cnt;
}
}
else if(x<f[p].ans){
f[p].lans++;
if(f[f[p].l].flag){
add(f[p].l,x);
}
else{
f[++cnt]=node{0,0,1,x,1,0};
f[p].l=cnt;
}
}
else f[p].cnt++;
}
int getmin(int p){
if(f[f[p].l].flag)return getmin(f[p].l);
return p;
}
bool remove(int p,int x){
if(!f[p].flag)return 0;
if(f[p].ans==x){
if(f[p].cnt>1)f[p].cnt--;
else{
if(f[f[p].l].flag&&f[f[p].r].flag){
int q=getmin(f[p].r);
f[p].ans=f[q].ans;
remove(f[p].r,f[q].ans);
}
else if(f[f[p].l].flag)f[p]=f[f[p].l];
else if(f[f[p].r].flag)f[p]=f[f[p].r];
else f[p].flag=0;
}
return 1;
}
else if(x>f[p].ans){
return remove(f[p].r,x);
}
else{
bool glaf=remove(f[p].l,x);
if(glaf)f[p].lans--;
return glaf;
}
}
int ranking(int p,int x){
if(!f[p].flag){
return 1;
}
if(f[p].ans==x){
return f[p].lans+1;
}
else if(x>f[p].ans){
return f[p].lans+f[p].cnt+ranking(f[p].r,x);
}
else{
return ranking(f[p].l,x);
}
}
int query(int p,int x){
if(f[p].lans+f[p].cnt<=x){
return query(f[p].r,x-f[p].lans-f[p].cnt);
}
else if(f[p].lans<=x){
return f[p].ans;
}
return query(f[p].l,x);
}
void pre(int p,int x){
if(!f[p].flag)return;
if(f[p].ans<x){
Pre=f[p].ans;
pre(f[p].r,x);
}
else{
pre(f[p].l,x);
}
}
void suc(int p,int x){
if(!f[p].flag)return;
if(f[p].ans>x){
Suc=f[p].ans;
suc(f[p].l,x);
}
else{
suc(f[p].r,x);
}
}
signed main(){
m=read();
while(m--){
int op=read(),x=read();
if(op==5){
add(1,x);
now++;
}
else if(op==1){
print(ranking(1,x)),endl;
}
else if(op==2){
print(query(1,x-1)),endl;
}
else if(op==3){
Pre=-2147483647;
pre(1,x);
print(Pre),endl;
}
else{
Suc=2147483647;
suc(1,x);
print(Suc),endl;
}
}
}
1614

被折叠的 条评论
为什么被折叠?



