poj 3580 SuperMemo(伸展树splay)

伸展树挺复杂的,而且还有各种版本,这里找到一种教高效率的版本http://wenku.baidu.com/view/b9cc2c75a417866fb84a8ee4.html

它的意思是在查找节点x的同时就自顶向下地伸展。我写不出代码,有些地方还暂时没看懂,先留着吧。这个代码是抄网上的,750ms


#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N=200010,oo=1<<30;

struct node
{
    #define lc c[0]
    #define rc c[1]
    int w,sz,lz,mn,rev;//w->值,,,sz->size,,,lz->key总量的lazy,,,mn->最小值,,,rev->反转的lazy
    node *c[2],*f;//左右孩纸和父指针,,f 仅用来splay的回溯up(),,所以不需要时时维护
    void clear(int x);//新建节点赋值x,,并清空原值
    void dwn();//下传
    void up();//上传
    void add(int x)//权值增加x
    {
        w+=x;
        lz+=x;
        mn+=x;
    }
}te[N],*nil=te,*pe=te,*pt[2],*rt;
//te->树,,nil->空指针,,其值不变,,pe->当前指针,,,pt->左右链,,rt->根

void node::clear(int x)
{
    mn=w=x;
    lc=rc=f=nil;
    sz=1;
    lz=rev=0;
}

void node::dwn()
{
    if (rev)
    {
        swap(lc,rc);
        lc->rev^=1;
        rc->rev^=1;
        rev=0;
    }
    if(lz)
    {
        if (lc!=nil)
            lc->add(lz);
        if (rc!=nil)
            rc->add(lz);
        lz=0;
    }
}

void node::up()//向上更新,,同时含有清空节点的功能
{
    sz=lc->sz+rc->sz+1;
    mn=min(lc->mn,rc->mn);
    mn=min(mn,w);
}

inline void zig(node*&x,int w)//旋转x和x->c[w];更新x为新的父亲
{
    node *y=x->c[w];
    x->c[w]=y->c[!w];
    y->c[!w]=x;
    x->up();
    x=y;//x->up();//这个在finish中有更新,,
}
void finish(node*&x,int w)
{
    pt[w]->c[!w]=x->c[w];//链接
    x->c[w]=nil->c[!w];
    nil->c[!w]->f=x;
    if (pt[w]==nil)
        return;
    for (node *y=pt[w];y!=x;y=y->f)//更新
        y->up();
}
#define cmp(x,y) ((y==x->lc->sz+1)?-1:(y>x->lc->sz))
void splay(node*&x,int k)//将排名第k(x为根的子树中)的节点提根至x节点
{
    pt[0]=pt[1]=nil;//左右链为空
    while(true)
    {
        x->dwn();
        int w=cmp(x,k);
        if (w==-1)//找到k
            break;
        if (w== 1)//右子树
            k-=x->lc->sz+1;
        x->c[w]->dwn();
        if (w==cmp(x->c[w],k))
        {
            k-=w*(x->c[w]->lc->sz+1);//右子树,,再减,,
            zig(x,w);//旋转
        }
        pt[!w]->c[w]=x;//链接
        x->f=pt[!w];
        pt[!w]=x;//更新链
        x=x->c[w];//更新mid链根
    }
    finish(x,0);//连接左中右
    finish(x,1);
    x->up();
}

void del(int x)//删掉排名第x的节点,,,,
{
    splay(rt,x-1);
    splay(rt->rc,2);//两次提跟之后x就是root->rc->lc;
    rt->rc->lc=nil;//删掉
    rt->rc->up();
    rt->up();
}

void reverse(int x,int y)//旋转区间[x,y]
{
    splay(rt,x-1);
    splay(rt->rc,y-x+2);//提出[x,y]
    rt->rc->lc->rev^=1;//放标记
}

void insert(int x,int y)//排名第x的后面增加一个节点,,值为y,,
{
    splay(rt,x);
    splay(rt->rc,1);
    rt->rc->lc=++pe;
    pe->clear(y);
    rt->rc->up();
    rt->up();
}

int getmin(int x,int y)//求[x,y]的最小值
{
    splay(rt,x-1);
    splay(rt->rc,y-x+2);
    return rt->rc->lc->mn;
}

void add(int x,int y,int z)//[x,y]内所有节点增加z,,
{
    splay(rt,x-1);
    splay(rt->rc,y-x+2);
    rt->rc->lc->add(z);
    rt->rc->up();
    rt->up();
}

void revolve(int x,int y,int z)//交换[x,x+z]和[x+z+1,y]
{//这里一般有两个算法,,1,提根出[x,y],在[x,y]中把x+z+1提根,,这样出现[x,x+z];,,删掉[x,x+z],将y提根,,将[x,x+z]接到y的右边
                     //2,把x+z+1提根,,左边把x-1提根,,右边把y+1提根,,这样出现了[x,x+z]和[x+z+1,y],,交换之
    splay(rt,x-1);
    splay(rt->rc,y-x+2);
    node*&p=rt->rc->lc;//p就是[x,y]
    splay(p,z+1);
    node *q=p->lc;//q就是[x,x+z],用来保存;
    p->lc=nil;
    p->up();
    splay(p,p->sz);//把y提根
    p->rc=q;
    p->up();
    rt->rc->up();
    rt->up();
}

int da[N],n,q;//data[]
void bld(node *&p,int l,int r)
{
    if(l>r)
        return;
    int mid=(l+r)/2;
    p=++pe;
    pe->clear(da[mid]);
    bld(p->lc,l,mid-1);
    bld(p->rc,mid+1,r);
    p->up();
}
int main()
{
    nil->sz=0;
    nil->mn=nil->w=oo;
    scanf("%d",&n);
    n+=2;
    int i;
    for(i=2;i<n;i++)
        scanf("%d",&da[i]);
    da[1]=da[n]=oo;
    bld(rt,1,n);

    for(scanf("%d",&q);q--;)
    {
        char s[10];
        int x,y,z;
        scanf("%s",s);
        if(strcmp(s,"ADD")==0)
        {
            scanf("%d%d%d",&x,&y,&z);
            add(x+1,y+1,z);
        }
        else if(strcmp(s,"REVERSE")==0)
        {
            scanf("%d%d",&x,&y);
            reverse(x+1,y+1);
        }
        else if(strcmp(s,"REVOLVE")==0)
        {
            scanf("%d%d%d",&x,&y,&z);
            int len=y-x+1;
            z=(z%len+len)%len;
            if(z)
                revolve(x+1,y+1,len-z);
        }
        else if(strcmp(s,"INSERT")==0)
        {
            scanf("%d%d",&x,&y);
            insert(x+1,y);
        }
        else if(strcmp(s,"DELETE")==0)
        {
            scanf("%d",&x);
            del(x+1);
        }
        else
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",getmin(x+1,y+1));
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值