题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224
前驱后继的骚操作:找x的前驱,先插入一个x,找到x的排名(最小排名),输出x前一名的数,删除插入的x。
找后继,先插入一个x,找到x的排名(最大排名),输出x后一名的数,删除插入的x。
#include<algorithm>
#include<string.h>
#include<stdio.h>
using namespace std;
const double afa=0.75;//判断树是否平衡的天平
struct node
{
//sn总的节点个数,vn有效节点个数,sum左右儿子的个数,cut自身个数,flag自身是否存在,v权,f父亲,ch左右儿子
int ch[2],f,v,flag,cut,sum[2],vn,sn;
} A[100010];
pair<int,int>M[100010];
int root,tot,car,cun[100010],tt=0;
int kk()//找空间,在重建的时候,结构体的空间会空出,这里能重复利用
{
if(tt) return cun[tt--];
return ++tot;
}
void up(int r,int flag)//更新节点,flag=0单点修改,flag=1修改所有父类节点
{
if(r==0) return ;
int sl=A[r].ch[0],sr=A[r].ch[1],far=A[r].f;//sl左儿子,sr右儿子
A[r].sum[0]=A[sl].sum[0]+A[sl].sum[1]+A[sl].cut;
A[r].sum[1]=A[sr].sum[0]+A[sr].sum[1]+A[sr].cut;
A[r].vn=A[sl].vn+A[sr].vn+A[r].flag;
A[r].sn=A[sl].sn+A[sr].sn+1;
if(flag)up(far,flag);
return ;
}
void new_node(int &r,int far,int k,int c)//加入新的节点
{
r=kk();
A[r].f=far;
A[r].ch[0]=A[r].ch[1]=0;
A[r].sum[0]=A[r].sum[1]=0;
A[r].v=k;
A[r].flag=A[r].sn=A[r].vn=1;
A[r].cut=c;
// up(r);
return ;
}
int Find(int r,int k)//查找数值为k的位置
{
if(A[r].v<k&&A[r].ch[1]) return Find(A[r].ch[1],k);
if(A[r].v>k&&A[r].ch[0]) return Find(A[r].ch[0],k);
return r;
}
void dfs(int r)//拍平,把子树拍平
{
if(A[r].ch[0]) dfs(A[r].ch[0]);
if(A[r].flag)
{
M[++car].first=A[r].v;
M[car].second=A[r].cut;
}
cun[++tt]=r;
if(A[r].ch[1]) dfs(A[r].ch[1]);
return ;
}
void go(int &g,int l,int r,int far)//重建节点
{
if(l>r) return ;
int mid=(l+r)>>1;
new_node(g,far,M[mid].first,M[mid].second);
go(A[g].ch[0],l,mid-1,g);
go(A[g].ch[1],mid+1,r,g);
up(g,0);
return ;
}
void rebulid(int r)//暴力建树,(暴力既是优雅)
{
int far=A[r].f;
car=0;
dfs(r);
if(r==root) go(root,1,car,0);
else go(A[far].ch[A[far].ch[1]==r],1,car,far);
return ;
}
void find_rebuild(int r,int k)//判断是否重建
{
int sl=A[r].ch[0],sr=A[r].ch[1];
if(double(A[sl].sn)>double(A[r].sn)*afa||//如果 x的左(右)子树的节点数量 > 以x为根的子树的
double(A[sr].sn)>double(A[r].sn)*afa||// 节点数量*α,那么,以x为根的这棵子树就是不平衡的
double(A[r].sn)*0.7>A[r].vn)//,假如在一棵子树中,有超过30%的点被删除了,那么就把这棵树重建。
{
rebulid(r);
return ;
}
if(A[r].v!=k) find_rebuild(A[r].ch[A[r].v<k],k);
return ;
}
void Insert(int k)//插入一个数值为k的数据
{
int r=Find(root,k);
if(A[r].v==k)//如果数据已经在树中存在
{
A[r].cut++;
A[r].flag=true;
up(r,1);
return ;
}
new_node(A[r].ch[A[r].v<k],r,k,1);//如果不存在,建立新的节点
up(A[r].ch[A[r].v<k],1);//更新
find_rebuild(root,k);
return ;
}
void del(int k)//删除k
{
int r=Find(root,k);
if(A[r].v!=k) return ;
if(A[r].cut)A[r].cut--;
if(A[r].cut==0) A[r].flag=false;
up(r,1);
return ;
}
int hou;//查找后继专用
int ranking(int k)//查找k的排名,从小到大
{
int ans=0,r=root;
while(A[r].v!=k)
{
if(r==0)
return -1;
if(A[r].v<k)
{
ans+=(A[r].sum[0]+A[r].cut);
r=A[r].ch[1];
}
else
{
r=A[r].ch[0];
}
}
hou=A[r].cut;
return ans+A[r].sum[0]+1;
}
int kth(int k)//查找第k大的数
{
int r=root;
while(k<=A[r].sum[0]||k>A[r].sum[0]+A[r].cut)
{
// printf("%d %d %d\n",k,A[r].v,A[r].sum[0]);
if(r==0)
return -1;
if(k<=A[r].sum[0])
{
r=A[r].ch[0];
}
else
{
k-=(A[r].sum[0]+A[r].cut);
r=A[r].ch[1];
}
}
return A[r].v;
}
int main()
{
int n,opt,x;
root=tot=0;
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&opt,&x);
if(opt==1) root==0?new_node(root,0,x,1):Insert(x);
if(opt==2) del(x);
if(opt==3) printf("%d\n",ranking(x));
if(opt==4) printf("%d\n",kth(x));
if(opt==5)//找前驱,插入一个数,x,找到x的排名,查找x排名的前一个数
{
Insert(x);
printf("%d\n",kth(ranking(x)-1));
del(x);
}
if(opt==6)//与上同理
{
Insert(x);
printf("%d\n",kth(ranking(x)+hou));
del(x);
}
}
}