Splay
涉及了区间翻转操作,Splay不再是BST;Splay只能保证其中序遍历为当前序列;用lazy标记做,具体见OI-wiki,代码见下
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
struct Splay
{
int l,r;
int cnt,Size;
int val,lazy;
}a[N];
int tot,n,INF=0x7fffffff,root,fa[N],m;
int New(int val)
{
a[++tot].val=val;
a[tot].cnt=a[tot].Size=1;
a[tot].lazy=0;
return tot;
}
void update(int p)
{
a[p].Size=a[a[p].l].Size+a[a[p].r].Size+a[p].cnt;
}
void build()
{
New(INF),New(-INF);
a[1].l=2,fa[2]=1;//记录每个点的父亲,这样方便splay操作
root=1;
update(1);
}
void tagrev(int x)
{
swap(a[x].l,a[x].r);
a[x].lazy^=1;
}
void pushdown(int x)
{
if(a[x].lazy)
{
tagrev(a[x].l),tagrev(a[x].r);
a[x].lazy=0;
}
}
pair<int,int> getval(int p,int rank)//first是答案val,second是得到答案的点,最后会进行splay
{
pushdown(p);
//这里的下传操作不同于线段树,放在最前面
//因为splay的时候会将当前节点父亲的某一个儿子p变成当前节点的某一个儿子
//而父亲可能没有lazy标记,但是由于pushdown放在了后面,当前节点有lazy标记
//也就是说p本来不用交换,但是变成当前节点的儿子之后,由于当前节点有lazy标记,我们就误认为p会交换了
//所以要将pushdown放在最前面
if(rank==a[a[p].l].Size+a[p].cnt) return make_pair(a[p].val,p);
if(rank<=a[a[p].l].Size) return getval(a[p].l,rank);
return getval(a[p].r,rank-(a[a[p].l].Size+a[p].cnt));
}
int get(int x)//判断x是其父亲的左儿子还是右儿子
{
if(a[fa[x]].l==x) return 0;//左儿子
else return 1;//右儿子
}
void zig(int p)//右旋(注意这里没有引用了)
{
int q=a[p].l,flag=get(p);
a[p].l=a[q].r;
if(a[q].r) fa[a[q].r]=p;
a[q].r=p,fa[q]=fa[p],fa[p]=q;
if(fa[q])
{
if(!flag) a[fa[q]].l=q;
else a[fa[q]].r=q;
}
update(p),update(q);
}
void zag(int p)//左旋
{
int q=a[p].r,flag=get(p);
a[p].r=a[q].l;
if(a[q].l) fa[a[q].l]=p;
a[q].l=p,fa[q]=fa[p],fa[p]=q;
if(fa[q])
{
if(!flag) a[fa[q]].l=q;
else a[fa[q]].r=q;
}
update(p),update(q);
}
void splay(int x,int goal=0)
{
if(!goal) root=x;
for(int f=fa[x];f!=goal;f=fa[x])
if(fa[f]!=goal)
{
bool sonx=get(x),sonf=get(f);
if(sonx==sonf)//一条链的情况
{
if(!sonx) zig(fa[f]),zig(f);
else zag(fa[f]),zag(f);
}
else//中间有折点的情况
{
if(!sonx) zig(f),zag(fa[x]);
else zag(f),zig(fa[x]);
}
}
else
{
bool sonx=get(x);
if(!sonx) zig(f);
else zag(f);
}
}
int insert(int &p,int f,int val)//插入操作,f为父亲,返回值为splay的点
{
if(!p)
{
p=New(val);
fa[p]=f;//这个别忘
return p;
}
if(val==a[p].val)
{
a[p].cnt++,a[p].Size++;
return p;
}
int res;
if(val<a[p].val) res=insert(a[p].l,p,val);
else res=insert(a[p].r,p,val);
update(p);
return res;
}
void Reverse(int l,int r)
{
pair<int,int> L=getval(root,l-1),R=getval(root,r+1);
//注意在geval之后,L到根,R到根的路径上的所有点的左右子树已经交换了,都没有懒标记
splay(L.second),splay(R.second,L.second);
tagrev(a[a[root].r].l);
}
void print(int x)
{
if(!x) return;
pushdown(x);
print(a[x].l);
if(a[x].val>=1&&a[x].val<=n) printf("%d ",a[x].val);
print(a[x].r);
}
int main()
{
build();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) splay(insert(root,0,i));
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
l++,r++;
Reverse(l,r);
}
print(root);
return 0;
}
yxc的实现也许可以借鉴一下,没有听过他讲代码的实现,可能比上面的实现更好
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构