poj 3580 SuperMemo (splay模板)

Posted hooying

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj 3580 SuperMemo (splay模板)相关的知识,希望对你有一定的参考价值。

题意: 给出一个数字序列,有6种操作:
(1) ADD x y d: 第x个数到第y个数加d 。
(2) REVERSE x y : 将区间[x,y]中的数翻转 。
(3) REVOLVE x y t :将区间[x,y]旋转t次。
(4) INSERT x p :在第x个数后面插入p 。
(5)DELETE x :删除第x个数 。
(6) MIN x y : 查询区间[x,y]中的最小值 。
思路:splay板子题,对于区间[l,r],将l-1旋为树根,将r+1旋为根的右孩子,那么r+1的左子树就是区间[l,r]。
区间旋转就是把区间分成两段,然后交换一下,因此我们可以把后一段区间处理到一个子树上,保存然后删除,接着再处理到前一段区间的前面。

#include<cstdio>
#include<iostream>
using namespace std;
const int maxx = 2e5+10;
const int inf = 0x3f3f3f3f;
int ch[maxx][2],fa[maxx],siz[maxx],key[maxx];
int rev[maxx],lazy[maxx],w[maxx],mi[maxx];
int rt,sz;
char s[20];
int get(int x)
{
    return ch[fa[x]][1]==x;
}
void update(int x)
{
    if(!x)return;
    siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
    mi[x]=key[x];
    if(ch[x][0])mi[x]=min(mi[x],mi[ch[x][0]]);
    if(ch[x][1])mi[x]=min(mi[x],mi[ch[x][1]]);
}
void pushdown(int x)
{
    if(!x)return;
    if(rev[x])
    {
        rev[ch[x][0]]^=1;
        rev[ch[x][1]]^=1;
        swap(ch[x][0],ch[x][1]);
        rev[x]=0;
    }
    if(lazy[x])
    {
        lazy[ch[x][0]]+=lazy[x];
        lazy[ch[x][1]]+=lazy[x];
        key[ch[x][0]]+=lazy[x];
        key[ch[x][1]]+=lazy[x];
        mi[ch[x][0]]+=lazy[x];
        mi[ch[x][1]]+=lazy[x];
        lazy[x]=0;
    }
}
void rotate(int x)
{
    int y=fa[x],z=fa[y],k=get(x);
    pushdown(x);pushdown(y);
    ch[y][k]=ch[x][k^1];fa[ch[y][k]]=y;
    ch[x][k^1]=y;fa[y]=x;fa[x]=z;
    if(z)ch[z][ch[z][1]==y]=x;
    update(y);update(x);
}
void splay(int x,int goal) 
{
    for(int y;(y=fa[x])!=goal;rotate(x))
        if(fa[y]!=goal)rotate((get(x)==get(y))?y:x);
    if(goal==0)rt=x; //注意
}
int findkth(int k) //找第k个节点
{
    int x=rt;
    while(1)
    {
        pushdown(x);
        if(k<=siz[ch[x][0]])x=ch[x][0];
        else
        {
            k-=siz[ch[x][0]]+1;
            if(!k)return x;
            x=ch[x][1];
        }
    }
}
int build(int l,int r,int f)
{
    if(l>r)return 0;
    int mid=(l+r)/2;
    int x=++sz;
    fa[x]=f;
    key[x]=w[mid];
    ch[x][0]=build(l,mid-1,x);
    ch[x][1]=build(mid+1,r,x);
    update(x);
    return x;
}
void add(int l,int r,int val) //区间加
{
    int x=findkth(l-1),y=findkth(r+1);
    splay(x,0);splay(y,x);
    int tmp=ch[ch[rt][1]][0];
    key[tmp]+=val;
    mi[tmp]+=val;
    lazy[tmp]+=val;
    update(ch[rt][1]);
    update(rt);     
}
void Insert(int k,int val) //在第k个数后插入值为x的节点
{
    int x=findkth(k),y=findkth(k+1);
    splay(x,0);splay(y,x);
    ch[ch[rt][1]][0]=++sz;
    fa[sz]=ch[rt][1];
    key[sz]=mi[sz]=val;
    siz[sz]=1;
    update(ch[rt][1]);
    update(rt);
}
void Delete(int k) //删除第k个节点
{
    int x=findkth(k-1),y=findkth(k+1);
    splay(x,0);splay(y,x);
    ch[ch[rt][1]][0]=0;
    update(ch[rt][1]);
    update(rt);
}
void Reverse(int l,int r) //区间翻转
{
    int x=findkth(l-1),y=findkth(r+1);
    splay(x,0); //x旋转为根
    splay(y,x); //y旋转为根的右孩子
    //旋转完之后y的左子树为区间[l,r]
    rev[ch[ch[rt][1]][0]]^=1; //lazy标记是否要旋转其左右子树
}
void revolve(int l1,int r1,int l2,int r2) //区间旋转
{
    int x=findkth(l2-1),y=findkth(r2+1);
    splay(x,0);splay(y,x);
    int tmp=ch[ch[rt][1]][0];ch[ch[rt][1]][0]=0; 
    update(ch[rt][1]);update(rt);
    x=findkth(l1-1);y=findkth(l1);
    splay(x,0);splay(y,x);
    ch[ch[rt][1]][0]=tmp;
    fa[tmp]=ch[rt][1];
    update(ch[rt][1]);update(rt);
}
int getmi(int l,int r) //找区间最小值
{
    int x=findkth(l-1),y=findkth(r+1);
    splay(x,0);splay(y,x);
    return mi[ch[ch[rt][1]][0]];
}
int main()
{
    int n,m;
    scanf("%d",&n);
    w[1]=-inf;w[n+2]=inf;
    for(int i=2;i<=n+1;i++)scanf("%d",&w[i]);
    rt=build(1,n+2,0);
    scanf("%d",&m);
    int x,y,z;
    while(m--)
    {
        scanf("%s",s);
        if(s[0]=='A')scanf("%d%d%d",&x,&y,&z),add(x+1,y+1,z);
        else if(s[0]=='I')scanf("%d%d",&x,&y),Insert(x+1,y);
        else if(s[0]=='D')scanf("%d",&x),Delete(x+1);
        else if(s[0]=='M')scanf("%d%d",&x,&y),printf("%d
",getmi(x+1,y+1));
        else if(s[3]=='E')scanf("%d%d",&x,&y),Reverse(x+1,y+1);
        else
        {
            scanf("%d%d%d",&x,&y,&z);
            z=z%(y-x+1);
            if(z)revolve(x+1,y-z+1,y-z+2,y+1);
        }
    }
    return 0;
}

以上是关于poj 3580 SuperMemo (splay模板)的主要内容,如果未能解决你的问题,请参考以下文章

poj 3580 SuperMemo (Splay)

POJ 3580(SuperMemo-Splay区间加)[template:Splay V2]

POJ 3580 SuperMemo (Splay)

poj 3580 SuperMemo (splay模板)

POJ3580:SuperMemo

POJ 3580SuperMemo