splay 新模板 【bzoj3223】 文艺平衡树

21 篇文章 0 订阅
2 篇文章 0 订阅

上次写splay拿noi2015维修数列来练手,当时觉得自己写得还挺好的,但是后来发现那个模板居然不能拿来写LCT(其实应该也可以写的,但是我是蒟蒻,我不会)。所以又发愤图强,去各大神犇博客中学习,终于写出了新的模板。

与之前的不同:
1、设虚节点,可以降低崩溃的几率;
2、旋转和splay等函数不需要打引用;
3、splaying用循环的方式,代替原来递归的方式,省空间。

注意事项:
1、在写build函数时(不只是splay,写指针版线段树这样的东西时也要注意)splay一定要打引用,否则就相当于这棵树没有建;
2、Merge和Split操作最好还是特判一下,虽然有点麻烦,但是就两行;
3、在寻找地址的时候顺便push_down,要不然错误会千奇百怪层出不穷,当然,我们也应该清楚的认识到矛盾具有特殊性,要具体问题具体分析。

模板代码如下:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,l,r;
struct splay{
    splay *ch[2],*fa;
    int val,sz;
    bool flip;
    splay(int);
    int cmp(int k)
    {
        if(k<=ch[0]->sz)  return 0;
        if(k>ch[0]->sz+1) return 1;
        return -1;
    }
    void maintain() {sz=1+ch[0]->sz+ch[1]->sz;}
    splay* find_it(int k)
    {
        push_down();
        int d=cmp(k);
        if(d==-1) return this;
        if(d==1) k-=ch[0]->sz+1;
        return ch[d]->find_it(k);
    }
    void push_down();
    int dir();
}*null=new splay(0),*V,*x,*y;
splay :: splay(int x)
{
    sz=null?1:0;
    val=x;flip=false;
    ch[0]=ch[1]=fa=null;
}
void splay :: push_down()
{
    if(!flip) return;
    flip=false;
    swap(ch[0],ch[1]);
    if(ch[0]!=null) ch[0]->flip=!ch[0]->flip;
    if(ch[1]!=null) ch[1]->flip=!ch[1]->flip;
    return;
}
int splay :: dir()
{
    return fa->ch[0]==this?0:fa->ch[1]==this?1:-1;
}
void turn(splay *c,int d)
{
    splay *y=c->ch[d^1];
    c->ch[d^1]=y->ch[d];
    if(y->ch[d]!=null) y->ch[d]->fa=c;
    y->fa=c->fa;
    y->ch[d]=c;
    int k;
    if(~(k=c->dir())) c->fa->ch[k]=y;
    c->fa=y;
    c->maintain();
    y->maintain();
}

void splaying(splay *c)
{
    int d;
    while(~(d=c->dir()))
    {
        if(d==c->fa->dir()) turn(c->fa->fa,d^1);
        turn(c->fa,d^1);
    }
}
void build(splay *&c,int l,int r)
{
    if(l>r) return;
    int mid=l+r>>1;
    c=new splay(mid);
    build(c->ch[0],l,mid-1);
    build(c->ch[1],mid+1,r);
    if(c->ch[0]!=null) c->ch[0]->fa=c;
    if(c->ch[1]!=null) c->ch[1]->fa=c;
    c->maintain();
    return;
}
splay* Merge(splay *x,splay *y)
{
    if(x==null) return y;
    if(y==null) return x;
    splay *c=x->find_it(x->sz);
    splaying(c);
    c->ch[1]=y;
    y->fa=c;
    c->maintain();
    return c;
}
void Split(splay *V,splay *&x,splay *&y,int k)
{
    if(k<=0)     {x=null;y=V;return;}
    if(k>=V->sz) {x=V;y=null;return;}
    splay *c=V->find_it(k);
    splaying(c);
    y=c->ch[1];y->fa=null;
    x=c;       x->ch[1]=null;
    x->maintain();
    return;
}
void cs(splay *c)
{
    if(c==null) return;
    c->push_down();
    cs(c->ch[0]);
    printf("%d ",c->val);
    cs(c->ch[1]);
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    build(V,1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l,&r);
        Split(V,V,x,l-1);
        Split(x,x,y,r-l+1);
        if(x!=null) x->flip=!x->flip;
        V=Merge(V,x);
        V=Merge(V,y);
    }
    cs(V);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值