P3391 【模板】文艺平衡树(Splay)

Splay

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
struct node *nil;//防止真的访问了NULL导致re(从零开始的编码生活)
struct node
{
    int val;//节点值
    int size;//以他为根的节点的子树节点个数(包括他自己)
    int flag;//懒标记
    node *ch[2];//左右儿子
    void sum()//重新计算size
    {
        size=1+ch[0]->size+ch[1]->size;//因为这里我们定义了不会re的无意义空地址。所以就不用判断
    }
    int cmp(int kth)//比较该往那颗子树上走
    {
        int s=ch[0]->size;
        if(kth==s+1)//就是当前根
            return -1;
        return (kth <= s ? 0 : 1);
    }
}*root;
int n,m;
void init()//初始化
{
    nil=new node;
    nil->flag=0;
    nil->size=0;
    nil->val=0;
    nil->ch[0]=nil->ch[1]=nil;
    root=nil;
}
node *New()//申请内存并初始化
{
    node *res=new node;
    res->size=1;
    res->flag=0;
    res->ch[0]=res->ch[1]=nil;
    return res;
}
void rotato(node* &now,int base)//旋转函数。应为这里是引用指针。所以直接指向就可以了。不用顾忌其他东西辣( ̄▽ ̄)~*
{
    node *k=now->ch[base^1];//利用异或进行取反
    now->ch[base^1]=k->ch[base];
    k->ch[base]=now;
    now->sum();//先计算被旋转下去的根,再计算新的根
    k->sum();
    now=k;
}
void build(node* &now,int l,int r)//建树,一定要引用。l为左边界。
{
    if(l>r)
        return ;
    int mid=(l+r)>>1;
    if(now==nil)//如果为空就动态申请,当然也可以动态删除
    {
        now=New();
        now->val=mid;
    }
    build(now->ch[0],l,mid-1);//左右递归的建树
    build(now->ch[1],mid+1,r);
    now->sum();
    return ;
}
void push_down(node* &now)//下放lazy tag
{
    if(now!=nil&&now->flag)
    {
        swap(now->ch[0],now->ch[1]);
        now->ch[0]->flag^=1;
        now->ch[1]->flag^=1;
        now->flag=0;
    }
    return ;
}
void visit(node* &now)//中序便利
{
    if(now==nil)
        return ;
    push_down(now);
    visit(now->ch[0]);
    if(now->val!=0&&now->val!=n+1)
        printf("%d ",now->val);
    visit(now->ch[1]);
}
void splaykth(node* &now,int kth)//将以now为根的树中把第kth小的旋转到根上
{
    push_down(now);
    int d=now->cmp(kth);
    if(d!=-1)
    {
        push_down(now->ch[d]);
        if(d==1)
            kth-=now->ch[0]->size+1;//换根要重新计算kth,下同
        int d2=now->ch[d]->cmp(kth);
        if(d2!=-1)
        {
            if(d2==1)
                kth-=now->ch[d]->ch[0]->size+1;
            splaykth(now->ch[d]->ch[d2],kth);
            if(d==d2)
                rotato(now,d2^1),rotato(now,d^1);//一字型
            else
                rotato(now->ch[d],d2^1),rotato(now,d^1);//之字形
        }
        else
            rotato(now,d^1);//单旋
    }
}//其实这里应该要判空的,以防止非法情况出现。不过根据这个题。我们调用的时候没有非法情况
void Reverse(int l,int r)//翻转,这里因为有可能涉及到将1~n都翻转一遍。所以我们可以建两个哨兵节点,位置在0,n+1上。
{       //将l~r翻转
        //l,r为第l个数到第r个数(1~n中)
    splaykth(root,l);//把l-1翻上来
    splaykth(root->ch[1],r+1-root->ch[0]->size);//把r+1翻到右子树根的位置上。这样根节点右儿子的左子树上就是l~r。
    root->ch[1]->ch[0]->flag^=1;//打上懒标记
    return ;
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    build(root,0,n+1);
    int a,b;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        Reverse(a,b);
    }
    /*splaykth(root,7);
    cout<<root->val;*/
    visit(root);//中序遍历一波
}

转载于:https://www.cnblogs.com/Lance1ot/p/9047440.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值