Splay练习4——P3721 [AH2017/HNOI2017]单旋

本文介绍了一种使用线段树优化伸展树的方法,通过分析旋转对树形态的影响,得出将最小值或最大值节点旋转至根部时树的深度变化规律,并利用线段树高效维护这些信息。此外,还探讨了如何利用set进行节点插入操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

蒟蒻的垂死挣扎

orz这题估计考场上想到也写不出来。

首先splay显然不是正解,考虑每次旋转对于树的影响,发现把min或max旋上去,树的形态几乎不变,手玩一下就可以知道这个规律,然后对于深度而言,min或max为根的子树深度不变,其他点深度+1即可,那么我们直观的想到用线段树维护深度信息,对于代价的计算就完成了。

然后考虑插点,明显不能按照题面去转,大力学一个 set ,然后就可以用set轻易地找到前驱及后继,这样我们就可以以一个极为舒服的姿势ac此题。

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#define RG register
#define N 200100
#define ls (now<<1)
#define rs ((now<<1)|1)
#define Ls s[x][0]
#define Rs s[x][1]
#define ll long long
#define ld long double
using namespace std;

inline int read(){
  RG int x=0,o=1; RG char ch=getchar();
  while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  if(ch=='-') o=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x*o;
}

int fa[N],s[N][2],m,tot,yyb[N],dep[N],root;
struct tree { int le,ri; int lz; } tr[N<<1];
struct mona { int op; int w;     } ps[N];

inline void yyb_Orz(){
    sort(yyb+1,yyb+tot+1);
    RG int len=unique(yyb+1,yyb+1+tot)-yyb-1;
    for(RG int i=1;i<=m;++i)
        if(ps[i].op==1) ps[i].w=lower_bound(yyb+1,yyb+1+len,ps[i].w)-yyb;
}

inline void Pushlazy(RG int now,RG int w) { tr[now].lz+=w; }
inline void Pushdown(RG int now){
    if(!tr[now].lz) return ;
    Pushlazy(ls,tr[now].lz),Pushlazy(rs,tr[now].lz);
    tr[now].lz=0;
}

inline void Build(RG int now,RG int le,RG int ri){
    tr[now]=(tree) { le,ri };
    if(le==ri) return ;
    RG int mid=(le+ri)>>1;
    Build(ls,le,mid),Build(rs,mid+1,ri);
}

inline void Update(RG int now,RG int le,RG int ri,RG int w){
    if(tr[now].le==le&&tr[now].ri==ri){
        Pushlazy(now,w); return ;
    }Pushdown(now); RG int mid=(tr[now].le+tr[now].ri)>>1;
    if(ri<=mid) Update(ls,le,ri,w);
    else if(le>mid) Update(rs,le,ri,w);
    else Update(ls,le,mid,w),Update(rs,mid+1,ri,w);
}

inline int Query(RG int now,RG int k){
    if(tr[now].le==tr[now].ri) return tr[now].lz;
    Pushdown(now);
    RG int mid=(tr[now].le+tr[now].ri)>>1;
    if(k<=mid) return Query(ls,k);
    else return Query(rs,k);
}

inline void Setval(RG int k,RG int w){ RG int sum=Query(1,k); Update(1,k,k,w-sum); }
inline int  Getval(RG int k)         { return     Query(1,k); }

set<int> S;
inline int Ins(RG int k,RG int op,RG int f){
    s[f][op]=k,fa[k]=f; int sum=Getval(f);
    Setval(k,sum+1),S.insert(k); return sum+1;
}

inline int Insert(RG int k){
    if(!root) { root=k,S.insert(k),Setval(k,1); return 1; }
    set<int>::iterator it;
    it=S.begin(); if(k<*it) return Ins(k,0,*it);
    it=S.end(),--it; if(k>*it) return Ins(k,1,*it);
    it=S.lower_bound(k); if(!s[*it][0]) return Ins(k,0,*it);
    --it; return Ins(k,1,*it);
}

inline int Splay_mmin(){
    set<int>::iterator it=S.begin();
    RG int sum=Getval(*it); if(root==*it) return 1;
    Update(1,fa[*it],tot,1),Setval(*it,1);
    if(s[*it][1]) fa[s[*it][1]]=fa[*it]; s[fa[*it]][0]=s[*it][1];
    s[*it][1]=root,fa[*it]=0,fa[root]=*it,root=*it; return sum;
}
inline int Splay_mmax(){
    set<int>::iterator it=S.end(); --it;
    RG int sum=Getval(*it); if(root==*it) return 1;
    Update(1,1,fa[*it],1),Setval(*it,1);
    if(s[*it][0]) fa[s[*it][0]]=fa[*it]; s[fa[*it]][1]=s[*it][0];
    s[*it][0]=root,fa[*it]=0,fa[root]=*it,root=*it; return sum;
}

inline int Del_mmin(){
    RG int sum=Splay_mmin(); S.erase(root);
    int las=root; root=s[root][1],s[las][1]=0,fa[root]=0;
    Update(1,1,tot,-1); return sum;
}
inline int Del_mmax(){
    RG int sum=Splay_mmax(); S.erase(root);
    int las=root; root=s[root][0],s[las][0]=0,fa[root]=0;
    Update(1,1,tot,-1); return sum;
}


int main(){
    m=read();
    for(RG int i=1;i<=m;++i){
        ps[i].op=read();
        if(ps[i].op==1) yyb[++tot]=ps[i].w=read();
    } yyb_Orz(),Build(1,1,tot);
    for(RG int i=1;i<=m;++i){
        if(ps[i].op==1) printf("%d\n",Insert(ps[i].w));
        if(ps[i].op==2) printf("%d\n",Splay_mmin());
        if(ps[i].op==3) printf("%d\n",Splay_mmax());
        if(ps[i].op==4) printf("%d\n",Del_mmin());
        if(ps[i].op==5) printf("%d\n",Del_mmax());
    } return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值