当前位置: 首页 > news >正文

P3721 [AHOI2017/HNOI2017] 单旋 - Link

题意

有一个单旋的 \(splay\),叫做 \(spaly\)。节点数 \(n\le 10^5\)
以下几种操作,共 \(m\) 次。

  1. \(key\) 加入一个值为 \(key\) 的点,\(a\le10^9\),任意两次 \(1\) 操作的 \(key\) 互不相同。
  2. 将最小值旋到根,输出旋转前的深度。
  3. 将最大值旋到根,输出旋转前的深度。
  4. 将最小值旋到根,输出旋转前的深度,然后把根删除。
  5. 将最小值旋到根,输出旋转前的深度,然后把根删除。

\(n,m\le10^5\)

思路

先把所有关键码离散化。
set 维护点,线段树维护深度,用数组维护 spaly 的形态。

插入 \(a\)

如果没有根,把这个节点当作根。
否则找到 \(a\) 的前驱 \(p\) 和后继 \(q\),分类讨论:

  1. 前驱后继都存在
    那么祂们中的某一个一定是另一个的祖先。如果 \(p\) 是祖先,那么 \(p\) 的右儿子一定有值,\(q\) 的左儿子一定是空的,那么把 \(a\) 接到 \(q\) 的左儿子上。如果 \(q\) 是祖先,同理,把 \(a\) 接到 \(p\) 的右儿子上。
  2. 只存在前驱
    \(a\) 接到 \(p\) 的右儿子上。
  3. 只有后继
    \(a\) 接到 \(q\) 的左儿子上。
    找前驱和后继可以直接在 set 中查询,插入后 \(a\) 的深度即为祂父亲的深度加 \(1\),树的形态直接修改。

单旋最小值

找到最小值对应的点 \(x\)

形态

\(x\) 一定只有右儿子(或没有),把祂接到 \(x\) 的父亲的左儿子上。
\(x\) 的右儿子设为当前的根,父亲设为空。

深度

\(x\) 的右子树中的点深度不变,\(x\) 的深度变成 \(1\),其祂点的深度加 \(1\)
可以全局深度加 \(1\),然后给 \(x\) 的右子树减 \(1\),最后把 \(x\) 的深度赋值为 \(1\)
由于一个子树内的编号都是连续的,所以用线段树是好维护的。

单旋最大值

同理。

单旋删除最小值

先执行单旋最小值,然后把跟和祂的儿子断开,全局深度减 \(1\)

单旋删除最大值

同理。

代码

/*
Luogu P3721 [AHOI2017/HNOI2017] 单旋
2026-03-21
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{template<typename T>inline void read(T&x){x=0;char c=getchar();bool f=0;while(!isdigit(c)) c=='-'?f=1:0,c=getchar();while(isdigit(c)) x=x*10+c-'0',c=getchar();f?x=-x:0;}template<typename T>inline void write(T x){if(x==0){putchar('0');return ;}x<0?x=-x,putchar('-'):0;short st[50],top=0;while(x) st[++top]=x%10,x/=10;while(top) putchar(st[top--]+'0');}inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}inline void write(char c){putchar(c);}inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=100010;
int n,op[maxn],a[maxn],p[maxn],cnt_p;
class Segment_Tree{
private:int t[maxn*4];void down(int u){t[u<<1]+=t[u],t[u<<1|1]+=t[u];t[u]=0;}void update1(int u,int l,int r,int d,int z){if(l>d||r<d) return ;if(l==r){t[u]=z;return ;}down(u);int mid=l+r>>1;update1(u<<1,l,mid,d,z),update1(u<<1|1,mid+1,r,d,z);}void update2(int u,int l,int r,int ll,int rr,int z){if(ll>r||rr<l) return ;if(ll<=l&&r<=rr){t[u]+=z;return ;}down(u);int mid=l+r>>1;update2(u<<1,l,mid,ll,rr,z),update2(u<<1|1,mid+1,r,ll,rr,z);}int query(int u,int l,int r,int d){if(l>d||r<d) return 0;if(l==r) return t[u];down(u);int mid=l+r>>1;return query(u<<1,l,mid,d)+query(u<<1|1,mid+1,r,d);}
public:void update1(int d,int z){update1(1,1,n,d,z);}void update2(int l,int r,int z){update2(1,1,n,l,r,z);}int query(int d){return query(1,1,n,d);}
}t;
set<int>s;
int rt=0,ch[maxn][2],fa[maxn];
int insert(int x){s.insert(x);if(rt==0){rt=x;t.update1(x,1);return 1;}auto w=s.lower_bound(x);if(w!=s.begin()){w--;if(!ch[*w][1]) ch[fa[x]=*w][1]=x;w++;}if(!fa[x]) w++,ch[fa[x]=*w][0]=x;int ans=t.query(fa[x])+1;t.update1(x,ans);return ans;
}
int find_min(){int x=*s.begin(),ans=t.query(x);if(x==rt) return ans;t.update2(x+1,fa[x]-1,-1);t.update2(1,n,1);ch[fa[x]][0]=ch[x][1];fa[ch[x][1]]=fa[x];ch[x][1]=rt;fa[rt]=x;rt=x;t.update1(x,1);return ans;
}
int find_max(){int x=*s.rbegin(),ans=t.query(x);if(x==rt) return ans;t.update2(fa[x]+1,x-1,-1);t.update2(1,n,1);ch[fa[x]][1]=ch[x][0];fa[ch[x][0]]=fa[x];ch[x][0]=rt;fa[rt]=x;rt=x;t.update1(x,1);return ans;
}
void del_min(){write(find_min());t.update2(1,n,-1);s.erase(rt);fa[ch[rt][1]]=0;rt=ch[rt][1];
}
void del_max(){write(find_max());t.update2(1,n,-1);s.erase(rt);fa[ch[rt][0]]=0;rt=ch[rt][0];
}
signed main(){read(n);for(int i=1;i<=n;i++){read(op[i]);if(op[i]==1) read(a[i]),p[++cnt_p]=a[i];}sort(p+1,p+1+cnt_p);cnt_p=unique(p+1,p+1+cnt_p)-p-1;for(int i=1;i<=n;i++) if(op[i]==1) a[i]=lower_bound(p+1,p+1+cnt_p,a[i])-p;for(int i=1;i<=n;i++){if(op[i]==1) write(insert(a[i]));if(op[i]==2) write(find_min());if(op[i]==3) write(find_max());if(op[i]==4) del_min();if(op[i]==5) del_max();write("\n");}return 0;
}
http://www.jsqmd.com/news/652090/

相关文章:

  • 2026年全自动波峰焊接驳台,哪家定做厂家更靠谱? - 企业推荐官【官方】
  • CST微波工作室求解器怎么选?从电小天线到超电大RCS,一篇讲透6大求解器的实战选择指南
  • 在合肥找厂房找抖音啊豆说厂房选址 - 企业推荐官【官方】
  • 老司机带路:CentOS7+NVIDIA驱动离线部署的5个血泪教训(附诊断命令大全)
  • 穿越机 vs 航拍机:从飞控(Pixhawk/Betaflight)选择到机身布局的实战解析
  • redis 未授权访问 (CNVD-2015-07557)
  • IT运维人每日崩溃实录[特殊字符]
  • 2026年3月专业的气力输送系统企业推荐,介质阻挡离子发生器/触酶离子净化器/气力输送系统,气力输送系统产品哪家好 - 品牌推荐师
  • QQ空间导出助手:全面备份你的数字回忆
  • 【2026奇点大会独家解码】:AI情感陪伴技术的5大落地瓶颈与企业级部署清单
  • 【实战指南】Origin盗版弹窗终结方案:一键批处理与Hosts文件双管齐下
  • 外卖点餐|基于springboot + vue外卖点餐系统(源码+数据库+文档)
  • AI安全实践指南:如何避免智能系统的现实风险
  • Agent 动了你的数据库?聊聊工具权限这件要命的事
  • 掌握AI写教材核心,运用低查重技巧,轻松完成高质量教材编写!
  • BLDC驱动实战:从基础原理到高效控制策略
  • 从零开始:手把手教你用Verilog搭建一个可配置的Cache模块(以Vortex GPGPU为例)
  • 红外遥控NEC协议解码避坑指南:STM32 HAL库输入捕获中断的细节处理
  • 基于Vue 3与.NET 8.0的SignalR实时聊天室:JWT身份验证与WebSocket实战
  • 在边缘设备上跑通Qwen2.5-7B+Agent:我的高通QCS8550开发板实战记录(含Dify配置避坑)
  • WorkshopDL:免费下载Steam创意工坊模组的终极完整指南 [特殊字符]
  • 2026智能锡膏柜厂家推荐:面向SMT智能制造的选型参考 - 企业推荐官【官方】
  • 2026奇点AI语音助手实战指南(仅限首批参会者泄露的8项API调用规范)
  • 淘宝NPM镜像证书过期问题全面解析:从报错到多镜像源切换实战
  • Laravel2.x:被遗忘的PHP框架遗珠
  • excel文件作者怎么修改?6个实用方法,小白也能快速搞定
  • 收藏 | 程序员必看:用 Skills 解决大模型工作流中的 Prompt 痛点,提升效率与稳定性
  • 四线式I2C接口设计:提升抗噪能力与降低BOM成本的实践指南
  • 逆向工程实战:从反编译到Flag还原的完整路径解析
  • 2026年市场上小程序开发服务商排行榜单权威解析与合作指南 - 企业推荐官【官方】