POJ 3580 (伸展树)

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

题目大意:对一个序列进行以下六种操作。输出MIN操作的结果。

解题思路

六个操作,完美诠释了伸展树有多么吊。注意,默认使用Lazy标记,在pushdown中维护。

ADD操作:为x~y元素加一个d值。首先用split切出x~y元素。然后改变给切出的root->add,root->min,root->v。再merge进原序列。

REVERSE操作:把x~y元素反转。首先用split切出x~y元素,然后改变root->flip标记。再merge进原序列。

REVOLVE操作:把x~y元素偏移T位。注意T可以为负。负向左,正向右。

首先对T进行修正。T=(T%(r-l+1)+(r-l+1))%(r-l+1)),参考自cxlove大神,这样正负方向就一致了,而且解决了没必要的偏移。

这样问题就转化为把[x,y]序列变成left+[y-T+1,y]+[x,y-T]+right,同样参考自cxlove大神。

那么只要split出[x,y-T]就行了。

INSERT操作:在第x元素后插入P。首先切出[1,x],然后merge这个新的P元素,再merge右段。

注意一下,虽然伸展树是意义上的BST,但是伸展树一旦为了维护原有序列顺序,则可以不再遵循BST的左<=中<=右原则。

这里的INSERT就是,把P元素merge到右边是防止BST维护改变顺序(merge只对left进行BST维护)。

DELETE操作:切出左右两段,merge即可。

MIN操作:求x~y元素最小值。依赖于pushup(也就是maintain),每次变动都要维护root-v,root->left,root->right三部分的最小值。

首先切出[x,y],root->minn就是结果。

额外吐槽一下build操作,首先在0号位置加一个inf大的前置结点,这样就可以split出[x,y]这个段了,如果没有前置结点,则应该这么切[1,x-1],当x=1时就完蛋了。

build(0,n)。这样0号点就被放在了最左边。build的时候为了保持原序列顺序,根据位置进行二分build。在最初把伸展树的高度给压下去。

因为伸展树的平衡性能实在太差,如果build成链,那么就要完蛋啦。

#include "cstdio"
#include "cstdlib"
#include "time.h"
#include "queue"
#include "vector"
#include "cstring"
using namespace std;
struct node
{
    node *ch[2];
    long long v,s,minn,flip,add;
    node() {s=flip=add=0;minn=1<<29;}
    void maintain()
    {
        s=1;
        s+=ch[0]->s;
        s+=ch[1]->s;
        minn=min(v,min(ch[0]->minn,ch[1]->minn));
    }
    void push_down()
    {
        if(flip)
        {
            flip = 0;
            swap(ch[0], ch[1]);
            ch[0]->flip = !ch[0]->flip;
            ch[1]->flip = !ch[1]->flip;
        }
        if(add)
        {
            ch[0]->add+=add;ch[1]->add+=add;
            ch[0]->v+=add;ch[1]->v+=add;
            ch[0]->minn+=add;ch[1]->minn+=add;
            add=0;
        }
    }
    int cmp(int x)
    {
        int d = x-ch[0]->s;
        if(d == 1) return -1;
        return d <= 0 ? 0 : 1;
    }
};
node *null=new node();
node *root;
node *left,*mid,*right,*oo;
vector<int> ans;
long long arr[200005];
void rotate(node* &o,int d)
{
    node *k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;
    o->maintain();k->maintain();
    o=k;
}
void splay(node* &o,int k)
{
    o->push_down();
    int d=o->cmp(k);
    if(d==1) k-=o->ch[0]->s+1;
    if(d!=-1)
    {
        node *p=o->ch[d];
        p->push_down();
        int d2=p->cmp(k);
        if(d2!=-1)
        {
            int k2=(d2==0?k:k-p->ch[0]->s-1);
            splay(p->ch[d2],k2);
            if(d==d2) rotate(o,d^1);else rotate(o->ch[d],d);
        }
        rotate(o,d^1);
    }
}
node *merge(node* left,node *right)
{
    splay(left,left->s);
    left->ch[1]=right;
    left->maintain();
    return left;
}
void split(node *o, int k, node *&left, node *&right)
{
    splay(o, k);
    left = o;
    right = o->ch[1];
    o->ch[1] = null;
    left->maintain();
}
void build(int l,int r,node* &o)
{
    if(l>r) return;
    int mid=(l+r)>>1;
    o=new node;o->v=arr[mid];o->minn=arr[mid];o->s=1;o->ch[0]=o->ch[1]=null;
    build(l,mid-1,o->ch[0]);
    build(mid+1,r,o->ch[1]);
    o->maintain();
}
void INSERT(int x,int v)
{
    split(root,x+1,left,oo);
    node *tt=new node;
    tt->v=v;tt->s=1;tt->minn=v;tt->ch[0]=tt->ch[1]=null;
    root=merge(merge(left,tt),oo);
}
void DELETE(int x)
{
    split(root,x,left,oo);
    split(oo,1,mid,right);
    root=merge(left,right);
}
void MIN(int a,int b)
{
    split(root,a,left,oo);
    split(oo,b-a+1,mid,right);
    printf("%lld\n",mid->minn);
    root=merge(merge(left,mid),right);
}
void REVOLVE(int a,int b,int c)
{
    if(!c) return;
    node *tt;
    split(root,a,left,oo);
    split(oo,b-a+1,mid,right); //切出[a,b]
    split(mid,b-c-a+1,tt,oo);//切出[a,b-c]
    tt=merge(oo,tt);//合并即可
    root=merge(merge(left,tt),right);
}
void ADD(int a,int b,int c)
{
    //切出[a,b]
    if(!c) return;
    split(root,a,left,oo);
    split(oo,b-a+1,mid,right);
    mid->add+=c;
    mid->v+=c;
    mid->minn+=c;
    root=merge(merge(left,mid),right);
}
void REVERSE(int a,int b)
{
    split(root,a,left,oo);
    split(oo,b-a+1,mid,right);
    mid->flip^=1;
    root=merge(merge(left,mid),right);
}
int main()
{
    //freopen("0.in","r",stdin);
    //freopen("0.out","w",stdout);
    int n,m,l,r,x;
    char cmd[10];
    arr[0]=1<<29;//前置结点防止干扰
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%lld",&arr[i]);
        build(0,n,root);
        scanf("%d",&m);
        while(m--)
        {
            scanf("%s",cmd);
            if(!strcmp(cmd,"ADD"))
            {
                scanf("%d%d%d",&l,&r,&x);
                ADD(l,r,x);
            }
            if(!strcmp(cmd,"REVERSE"))
            {
                scanf("%d%d",&l,&r);
                REVERSE(l,r);
            }
            if(!strcmp(cmd,"REVOLVE"))
            {
                scanf("%d%d%d",&l,&r,&x);
                REVOLVE(l,r,(x%(r-l+1)+(r-l+1))%(r-l+1));
            }
            if(!strcmp(cmd,"INSERT"))
            {
                scanf("%d%d",&l,&x);
                INSERT(l,x);
            }
            if(!strcmp(cmd,"DELETE"))
            {
                scanf("%d",&l);
                DELETE(l);
            }
            if(!strcmp(cmd,"MIN"))
            {
                scanf("%d%d",&l,&r);
                MIN(l,r);
            }
        }
        root=left=right=oo=null;
    }
}
13476518 neopenx 3580 Accepted 8680K 1047MS C++ 4510B 2014-09-25 17:10:36
时间: 2024-10-04 05:50:03

POJ 3580 (伸展树)的相关文章

POJ 3468 伸展树建树

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 59628   Accepted: 18180 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

POJ 3580 SuperMemo(伸展树的基本操作)

题目大意:给你六个操作,让你实现这些功能. 解题思路:伸展树的基本应用,用伸展数实现各种功能. SuperMemo Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10404   Accepted: 3320 Case Time Limit: 2000MS Description Your friend, Jackson is invited to a TV show called SuperMemo in which t

poj 3580 SuperMemo splay树模板题

题意: 给一个序列,对其进行各种操作.在对序列仅对操作的处理上,splay是比线段树强大的,虽然均摊复杂度均为logN,但它能支持1:在某个位置插入一些连续的数,2:在某个位置删除一些连续的数.只是splay随便一些200+行. 分析: 网上各种模板介绍漫天飞,这个还算简洁明了. 代码: //poj 3580 #include <stdio.h> #define maxN 200000 int N,T,node; int a[maxN],size[maxN],left[maxN],right[

poj_3580 伸展树

自己伸展树做的第一个题 poj 3580 supermemo. 题目大意 对一个数组进行维护,包含如下几个操作: ADD x, y, d 在 A[x]--A[y] 中的每个数都增加d REVERSE x, y 将 A[x]--A[y] 中的数进行反转,变为 A[y],A[y-1]....A[x+1],A[x] REVOLVE x, y, T 将 A[x]--A[y]中的数连续右移T次 INSERT x, P 在A后添加数P DELETE x 删除A[x] MIN x, y 查询A[x]--A[y

poj 3580 SuperMemo (Splay)

poj 3580 好恶心的题目,真是各种操作都来了个遍 ... 不过Splay树真是一种神奇的东西,通过旋转就能实现各种操作,而且方法也都相差不大 . 题意: 给出一个数字序列,有6种操作: (1) ADD x y d: 第x个数到第y个数加d . (2) REVERSE x y : 将区间[x,y]中的数翻转 . (3) REVOLVE x y t :将区间[x,y]旋转t次,如1 2 3 4 5 旋转2次后就变成4 5 1 2 3 . (4) INSERT x p :在第x个数后面插入p .

伸展树总结

最近做的一些Splay题及思路 BZOJ 1588 就是求一个数的前驱和后继,用Splay很简单 POJ 3468  很经典的线段树题目,用Splay做练习懒惰标记 HDU 1890  涉及区间翻转,注意直接以数列下标建树,对原数列排序后,直接查找,找到后删除. HDU 3436  很好的一道题,首先离散化,Splay 树中每个节点表示的是一段区间, TOP:首先删除这个元素,再将其插入到队首 RANK:即寻找第K大,经典操作 QUERY:将该元素Splay 到根部,size[ch[root][

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

SuperMemo Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 11384   Accepted: 3572 Case Time Limit: 2000MS Description Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game

伸展树Splay

新学的,其实吧,就那么回事.... 看了几天,splay处理序列问题,真的非常厉害,翻转,插入,删除,线段树实现不了的功能,splay用起来很方便. POJ 3580 SuperMemo 这题基本就是检验模板的题,各种操作都有,错了好多次,发现以前写的代码有错了的,数据水点,给水过了,注意pushup. Splay模板 #include <cstdio> #include <cstring> #include <map> #include <algorithm&g

poj 3468 Splay 树

大二上的时候,写过一个AVL的操作演示,今天一看Splay,发现和AVL其实一样,加上线段树的基础,懒惰标记什么都知道,学起来轻松许多哦 我参考的模板来自这里  http://blog.csdn.net/u013480600/article/list/2 里面有大量的ch[r][0] ch[r][1]等 我建议用宏定义取代,写的时候方括号少打了很多,等做的题多得时候,我再把自己使用的模板发来 #include <cstdio> #include <cstring> #include