SuperMemo(Splay区间操作)

学习链接:https://blog.csdn.net/ztzshiwo_cnyali/article/details/52221503

题目链接:http://poj.org/problem?id=3580

POJ 3580

splay超全模板题...

总共有6种操作,分别为MIN,INSERT,ADD,DELETE,REVERSE,REVOLVE六种,下面会分别介绍格式与方法

首先输入N以及N个数,接下来输入M,即操作个数,接下来M行包含操作,具体格式如下

1.MIN a b 求a,b区间的最小值

2.ADD a b c 对a,b区间的每个数添加c

3.DELETE a 将第a个数删除

4.INSERT a b 在第a个数后面添加一个数b

5.REVERSE a,b 将a,b区间每个数翻转 如 REVERSE 2 4 (1,2,3,4,5)-->(1,4,3,2,5)

6.REVOLVE a,b,c 将a,b区间作为一个环 然后旋转c次(这是我的理解... ...)

如 REVOVLE 2 4 1 (1,2,3,4,5)-->(1,4,2,3,5)

REVOVLE 2 4 2 (1,2,3,4,5)-->(1,3,4,2,5)

然后这里面的c有可能是负数并且很大... ... 所以要取模

讲一下算法思想:

构建一棵splay树 储存父亲 左右儿子 翻转标记 增加标记 最小值 本身的值 以此节点为根的子树的节点数 (size)

首先将1~n节点添加进去 同时要在最前面和最后面添加一个节点(具体理由后面讲)

基本函数:update(更新这个节点的MIN值与size),splay(将一个点旋转到目标位置),rotateto(找到第k个节点并旋转到目标位置),pushdown(将增加标记(add)与翻转标记(rev)推到下一层节点),newnode(构建一个新节点,节点序号为cnt)

讲讲每个操作的具体操作方法

1.MIN a,b 将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,注意:因为前面添加了一个节点,所以原来第a个节点为现在的第a+1个节点 所以现在根节点右节点的左子树就代表以前的 a,b区间,只要输出它的MIN就可以了

2.ADD a,b,c 同理 将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树增加标记与数值,最小值添加c

3.DELETE a 将第a个节点旋到根节点,第a+2个节点旋到根节点的右节点,那么根节点右节点的左子树有且仅有a+1这个节点(即原来的第a个节点),那么断绝他们的父子关系就可以了

4.INSERT a b 将第a个节点旋到根节点,第a+1个节点旋到根节点的右节点,那么根节点右节点的左子树绝对没有节点,同样添加一个值为b的新节点,然后建立关系

5.REVERSE a,b 与ADD相似 只要改变一下将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树更改REV标记

6.REVOLVE a,b,c这个操作有一点复杂... ...实际上可以看出 这就是将这个区间里面分成两个部分然后交换位置,那我们先通过旋转找到这一段区间,将它从父亲节点那里并记录位置,然后又找到另一端区间的位置,将之前存下来区间放在这个区间的前面,就可以实现,这一段需要一定的理解,可以手动模拟一下发现规律,具体见代码

#include
#include
#include
#include
#include
const int maxn=400010;
using namespace std;
int rt,m,n,a[maxn],cnt;
int val[maxn],Min[maxn],add[maxn],rev[maxn];
int ls[maxn],rs[maxn],fa[maxn],sz[maxn];
void newnode(int v)
{
    sz[++cnt]=1;
    val[cnt]=Min[cnt]=v;
    fa[cnt]=ls[cnt]=rs[cnt]=add[cnt]=rev[cnt]=0;
}
void update(int x)
{
    sz[x]=sz[ls[x]]+sz[rs[x]]+1;
    Min[x]=val[x];
    if(ls[x]&&Min[ls[x]]Min[ls[x]];
    if(rs[x]&&Min[rs[x]]Min[rs[x]];
}
void pushdown(int x)
{
    if(add[x]!=0)
    {
        if(ls[x])
        {
            add[ls[x]]+=add[x];
            Min[ls[x]]+=add[x];
            val[ls[x]]+=add[x];
        }
        if(rs[x])
        {
            add[rs[x]]+=add[x];
            Min[rs[x]]+=add[x];
            val[rs[x]]+=add[x];
        }
        add[x]=0;
    }
    if(rev[x])
    {
        if(ls[x])rev[ls[x]]^=1;
        if(rs[x])rev[rs[x]]^=1;
        swap(ls[x],rs[x]);
        rev[x]=0;
    }
}
void build(int x,int f)
{
    if(x==n+2)return;
    newnode(a[x]);
    if(f)
    {
        fa[cnt]=f;
        rs[f]=cnt;
    }
    else rt=cnt;
    int now=cnt;
    build(x+1,cnt);
    update(now);
}
void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    build(0,0);
}
void rrotate(int x)
{
    int y=ls[x],k=fa[x];
    ls[x]=rs[y];
    if(ls[x])fa[ls[x]]=x;
    fa[x]=y;fa[y]=k;rs[y]=x;
    if(k==0)rt=y;
    else 
    if(ls[k]==x)ls[k]=y;
    else rs[k]=y;
    update(x);
}
void lrotate(int x)
{
    int y=rs[x],k=fa[x];
    rs[x]=ls[y];
    if(rs[x])fa[rs[x]]=x;
    ls[y]=x;fa[x]=y;fa[y]=k;
    if(k==0)rt=y;
    else 
    if(ls[k]==x)ls[k]=y;
    else rs[k]=y;
    update(x);
}
void splay(int x,int f)
{
    int y,z;
    while(1)
    {
        if((y=fa[x])==f)break;
        if((z=fa[y])==f)
        {
            if(ls[y]==x)rrotate(y);
            else lrotate(y);
        }
        else
        {
            if(ls[z]==y)
            {
                if(ls[y]==x)rrotate(z),rrotate(y);
                else lrotate(y),rrotate(z);
            }
            else
            {
                if(rs[y]==x)lrotate(z),lrotate(y);
                else rrotate(y),lrotate(z);
            }
        }
    }
    return;
}
void rotateto(int k,int f)
{
    int i=rt;
    while(1)
    {
        pushdown(i);
        int lsum=sz[ls[i]]+1;
        if(k==lsum)break;
        if(kls[i];
        else k-=lsum,i=rs[i];
    }
    splay(i,f);
}
void ADD(int x,int y,int z)
{
    rotateto(x,0);
    rotateto(y+2,rt);
    int k=ls[rs[rt]];
    add[k]+=z,val[k]+=z,Min[k]+=z;
}
void insert(int x,int y)
{
    rotateto(x+1,0);rotateto(x+2,rt);
    newnode(y);
    ls[rs[rt]]=cnt;
    fa[cnt]=rs[rt];
}
void del(int x)
{
    rotateto(x,0);rotateto(x+2,rt);
    ls[rs[rt]]=0;
}
void MIN(int x,int y)
{
    rotateto(x,0);rotateto(y+2,rt);
    printf("%d\n",Min[ls[rs[rt]]]);
}
void reverse(int x,int y)
{
    if(x==y)return;
    rotateto(x,0);rotateto(y+2,rt);
    rev[ls[rs[rt]]]^=1;
}
void revolve(int x,int y,int z)
{
    int p=(y-x+1),k,t;
    k=(z%p+p)%p;
    if(k)
    {
        rotateto(y-k+1,0);rotateto(y+2,rt);
        t=ls[rs[rt]];
        ls[rs[rt]]=0;
        rotateto(x,0);rotateto(x+1,rt);
        ls[rs[rt]]=t;
        fa[t]=rs[rt];
    }
}
void solve()
{
    int x,y,z;
    char s[10];
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        if(s[0]=='A')
        {
            scanf("%d%d%d",&x,&y,&z);
            ADD(x,y,z);
        }
        else
        if(s[0]=='I')
        {
            scanf("%d%d",&x,&y);
            insert(x,y);
        }
        else
        if(s[0]=='D')
        {
            scanf("%d",&x);
            del(x);
        }
        else
        if(s[0]=='M')
        {
            scanf("%d%d",&x,&y);
            MIN(x,y);
        }
        else
        if(s[0]=='R')
        {
            if(s[3]=='O')
            {
                scanf("%d%d%d",&x,&y,&z);
                revolve(x,y,z);
            }
            else
            {
                scanf("%d%d",&x,&y);
                reverse(x,y);
            }
        }
    }
    return;
}
int main()
{
    init();
    solve();
    return 0;
}

 

你可能感兴趣的:(SuperMemo(Splay区间操作))