[AH2017/HNOI2017]单旋
XXVI.[AH2017/HNOI2017]单旋
先从单旋最小/大值的操作看起。手动模拟一下的话就会发现它对整棵树的形态几乎没有影响,就是断开最小值与它父亲的连边,并用其原本的右儿子(如果存在)替代。之后,将整棵树的根设作其新右儿子。最大值同理。
然后删除最小值也类似。注意删除一个原本就在树顶的最小值意味着要更新树根。
现在考虑比较玄乎的插入操作。明显,其要么插入到前驱的右儿子,要么插入到后继的左儿子,并且依照建树的方法,其前驱与后继在未插入之前必然是父子关系。所以我们只需插入到其中更深的一个即可。
前驱后继随便开个 set
维护即可。断边连边并求深度可以用LCT维护。
(附:因为其并不会大幅度改变深度,最多只是在单旋最大最小值的时候将其余点的深度全部升高了一,但是这个可以通过打全局 tag
简单处理,所以实际上也可以不用 LCT
,不过那样讨论起来太麻烦,就懒得那么搞了)
时间复杂度 。
代码:
#include<bits/stdc++.h>
using namespace std;
int m,cnt,FA[100100],LS[100100],RS[100100],rt;
#define lson t[x].ch[0]
#define rson t[x].ch[1]
struct LCT{int ch[2],fa,sz;bool rev;}t[100100];
void REV(int x){swap(lson,rson),t[x].rev^=1;}
void pushdown(int x){if(!t[x].rev)return;if(lson)REV(lson);if(rson)REV(rson);t[x].rev=false;}
void pushup(int x){t[x].sz=t[lson].sz+t[rson].sz+1;}
int identify(int x){if(t[t[x].fa].ch[0]==x)return 0;if(t[t[x].fa].ch[1]==x)return 1;return -1;}
void rotate(int x){
int y=t[x].fa,z=t[y].fa,dirx=identify(x),diry=identify(y),b=t[x].ch[!dirx];
if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
if(b)t[b].fa=y;t[y].ch[dirx]=b;
t[x].ch[!dirx]=y,t[y].fa=x,pushup(y),pushup(x);
}
void pushall(int x){if(identify(x)!=-1)pushall(t[x].fa);pushdown(x);}
void splay(int x){for(pushall(x);identify(x)!=-1;rotate(x))if(identify(t[x].fa)!=-1)rotate(identify(t[x].fa)==identify(x)?t[x].fa:x);}
void access(int x){for(int y=0;x;x=t[y=x].fa)splay(x),rson=y,pushup(x);}
void makeroot(int x){access(x),splay(x),REV(x);}
void link(int x,int y){access(x),splay(x),t[x].fa=y;}
int split(int x,int y){makeroot(x),access(y),splay(y);return t[y].sz;}
void cut(int x,int y){split(x,y);t[y].ch[0]=t[x].fa=0,pushup(y);}
set<pair<int,int> >s;
int insert(int k){
int x=++cnt;if(s.empty()){s.emplace(k,x),rt=x,pushup(x);return 1;}
auto it=s.lower_bound(make_pair(k,0));int bef=-1,aft=-1;
if(it!=s.end())aft=it->second;if(it!=s.begin())bef=(--it)->second;s.emplace(k,x);
if(aft==-1){link(x,FA[x]=bef),RS[bef]=x;return split(rt,x);}
if(bef==-1){link(x,FA[x]=aft),LS[aft]=x;return split(rt,x);}
int BF=split(rt,bef),AF=split(rt,aft);link(x,FA[x]=(BF>AF?bef:aft)),(BF>AF?RS[bef]:LS[aft])=x;return max(BF,AF)+1;
}
int rotatemin(){
int x=s.begin()->second,ret=split(rt,x);
if(x!=rt){
LS[FA[x]]=RS[x],cut(x,FA[x]);
if(RS[x])cut(x,RS[x]),link(RS[x],FA[x]),FA[RS[x]]=FA[x];
link(x,rt),FA[x]=0,FA[rt]=x,RS[x]=rt,rt=x;
}
return ret;
}
int rotatemax(){
int x=s.rbegin()->second,ret=split(rt,x);
if(x!=rt){
RS[FA[x]]=LS[x],cut(x,FA[x]);
if(LS[x])cut(x,LS[x]),link(LS[x],FA[x]),FA[LS[x]]=FA[x];
link(x,rt),FA[x]=0,FA[rt]=x,LS[x]=rt,rt=x;
}
return ret;
}
int delmin(){
int x=s.begin()->second,ret=split(rt,x);s.erase(s.begin());
if(x!=rt){
cut(x,FA[x]),LS[FA[x]]=RS[x];
if(RS[x])cut(x,RS[x]),link(RS[x],FA[x]),FA[RS[x]]=FA[x];
FA[x]=0;
}else if(RS[x])cut(x,RS[x]),FA[rt=RS[x]]=0;else rt=0;
return ret;
}
int delmax(){
int x=s.rbegin()->second,ret=split(rt,x);s.erase(--s.end());
if(x!=rt){
cut(x,FA[x]),RS[FA[x]]=LS[x];
if(LS[x])cut(x,LS[x]),link(LS[x],FA[x]),FA[LS[x]]=FA[x];
FA[x]=0;
}else if(LS[x])cut(x,LS[x]),FA[rt=LS[x]]=0;else rt=0;
return ret;
}
int main(){
// freopen("4.in","r",stdin);
scanf("%d",&m);
for(int i=1,tp,x;i<=m;i++){
scanf("%d",&tp);
if(tp==1)scanf("%d",&x),printf("%d\n",insert(x));
if(tp==2)printf("%d\n",rotatemin());
if(tp==3)printf("%d\n",rotatemax());
if(tp==4)printf("%d\n",delmin());
if(tp==5)printf("%d\n",delmax());
// printf("-----%d-----\n",rt);
// for(int j=1;j<=cnt;j++)printf("%d:[%d,%d]:%d\n",j,LS[j],RS[j],FA[j]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?