替罪羊树模板

题目链接: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);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值