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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值