POJ 3580 - SuperMemo - [伸展树splay]

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

Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1A2, ... An}. Then the host performs a series of operations and queries on the sequence which consists:

  1. ADD x y D: Add D to each number in sub-sequence {Ax ... Ay}. For example, performing "ADD 2 4 1" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5, 5}
  2. REVERSE x y: reverse the sub-sequence {Ax ... Ay}. For example, performing "REVERSE 2 4" on {1, 2, 3, 4, 5} results in {1, 4, 3, 2, 5}
  3. REVOLVE x y T: rotate sub-sequence {Ax ... AyT times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
  4. INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5}
  5. DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
  6. MIN x y: query the participant what is the minimum number in sub-sequence {Ax... Ay}. For example, the correct answer to "MIN 2 4" on {1, 2, 3, 4, 5} is 2

To make the show more interesting, the participant is granted a chance to turn to someone else that means when Jackson feels difficult in answering a query he may call you for help. You task is to watch the TV show and write a program giving the correct answer to each query in order to assist Jackson whenever he calls.

Input

The first line contains (≤ 100000).

The following n lines describe the sequence.

Then follows M (≤ 100000), the numbers of operations and queries.

The following M lines describe the operations and queries.

Output

For each "MIN" query, output the correct answer.

Sample Input

5
1
2
3
4
5
2
ADD 2 4 1
MIN 4 5

Sample Output

5

题意:

给出五种操作:

  1. ADD x y D: 对区间[x,y]全部加上D;
  2. REVERSE x y: 反转区间[x,y];
  3. REVOLVE x y T: 将区间[x,y]向右循环平移T次,每次一格,例如 REVOLVE 2 4 2 在{1, 2, 3, 4, 5}操作结果为{1, 3, 4, 2, 5};
  4. INSERT x P: 在位置x后面插入一个数P;
  5. DELETE x: 删除位置x上的数;
  6. MIN x y: 求区间[x,y]中的最小值;

题解:

splay模板题,lazy标记模仿线段树标记区间加上多少,rev标记标记是否需要反转,mini[x]记录节点x统领的整棵子树(包含自己)的key[x]的最小值。

可能AC代码(这不怪我,僵B POJ又挂掉了,判不了题目):

/*
5
1
2
3
4
5
11
ADD 1 4 1
REVERSE 2 4
ADD 2 5 2
REVOLVE 2 4 2
DELETE 4
MIN 1 3
REVERSE 1 4
INSERT 2 4
REVOLVE 1 4 6
DELETE 2
MIN 1 4
*/
#include<cstdio>
#include<algorithm>
#define Key_value ch[ch[root][1]][0]
using namespace std;
const int maxn=2e5+10;
int n,m;
int a[maxn];

/******************************** splay - st ********************************/
int root,nodecnt;
int par[maxn],ch[maxn][2];
int key[maxn],mini[maxn],size[maxn];
int lazy[maxn]; //lazy标记
bool rev[maxn]; //反转标记
int pool[maxn],poolsize; //节点回收
void NewNode(int &x,int p,int k)
{
    if(poolsize>0) x=pool[--poolsize];
    else x=++nodecnt;
    par[x]=p;
    ch[x][0]=ch[x][1]=0;
    key[x]=k;
    mini[x]=k;
    size[x]=1;
    lazy[x]=0;
    rev[x]=0;
}
void Update_Rev(int x)
{
    if(x==0) return;
    swap(ch[x][0],ch[x][1]);
    rev[x]^=1;
}
void Update_Add(int x,int val)
{
    if(x==0) return;
    key[x]+=val;
    mini[x]+=val;
    lazy[x]+=val;
}
void Pushup(int x)
{
    size[x]=size[ch[x][0]]+size[ch[x][1]]+1;

    mini[x]=key[x];
    if(ch[x][0]) mini[x]=min(mini[x],mini[ch[x][0]]);
    if(ch[x][1]) mini[x]=min(mini[x],mini[ch[x][1]]);
}
void Pushdown(int x)
{
    if(rev[x])
    {
        Update_Rev(ch[x][0]);
        Update_Rev(ch[x][1]);
        rev[x]=0;
    }
    if(lazy[x])
    {
        Update_Add(ch[x][0],lazy[x]);
        Update_Add(ch[x][1],lazy[x]);
        lazy[x]=0;
    }
}
void Inorder(int x) //debug
{
    if(x==0) return;
    Pushdown(x);
    Inorder(ch[x][0]);
    printf("%d ",key[x]);
    Inorder(ch[x][1]);
    Pushup(x);
    if(x==root) printf("\n");
}
void Rotate(int x,int type) //旋转,0为左旋zag,1为右旋zig
{
    int y=par[x];
    Pushdown(y); Pushdown(x); //先把y的标记向下传递,再把x的标记往下传递
    ch[y][!type]=ch[x][type]; par[ch[x][type]]=y;
    if(par[y]) ch[par[y]][(ch[par[y]][1]==y)]=x;
    par[x]=par[y];
    ch[x][type]=y; par[y]=x;
    Pushup(y); Pushup(x);
}
void Splay(int x,int goal)
{
    Pushdown(x);
    while(par[x]!=goal)
    {
        if(par[par[x]]==goal)
        {
            Pushdown(par[x]); Pushdown(x);
            Rotate(x,ch[par[x]][0]==x); //左孩子zig,有孩子zag
        }
        else
        {
            Pushdown(par[par[x]]); Pushdown(par[x]); Pushdown(x);
            int y=par[x];
            int type=(ch[par[y]][0]==y); //type=0,y是右孩子;type=1,y是左孩子
            if(ch[y][type]==x)
            {
                Rotate(x,!type);
                Rotate(x,type);
            }
            else
            {
                Rotate(y,type);
                Rotate(x,type);
            }
        }
    }
    if(goal==0) root=x;
}
int Get_Kth(int x,int k) //得到第k个节点
{
    Pushdown(x);
    int t=size[ch[x][0]]+1;
    if(t==k) return x;
    if(t>k) return Get_Kth(ch[x][0],k);
    else return Get_Kth(ch[x][1],k-t);
}
int Get_Min(int x)
{
    Pushdown(x);
    while(ch[x][0])
    {
        x=ch[x][0];
        Pushdown(x);
    }
    return x;
}
int Get_Max(int x)
{
    Pushdown(x);
    while(ch[x][1])
    {
        x=ch[x][1];
        Pushdown(x);
    }
    return x;
}
void Build(int &x,int l,int r,int par) //建树,先建立中间结点,再建两端的方法
{
    if(l>r) return;
    int mid=(l+r)/2;
    NewNode(x,par,a[mid]);
    Build(ch[x][0],l,mid-1,x);
    Build(ch[x][1],mid+1,r,x);
    Pushup(x);
}
void Init() //初始化,前后各加一个空节点
{
    root=nodecnt=poolsize=0;
    par[root]=ch[root][0]=ch[root][1]=0;
    key[root]=size[root]=0;
    lazy[root]=rev[root]=0;
    NewNode(root,0,-1); //头部加入一个空位
    NewNode(ch[root][1],root,-1); //尾部加入一个空位
    Build(Key_value,1,n,ch[root][1]);
    Pushup(ch[root][1]);
    Pushup(root);
}
void Add(int l,int r,int val)
{
    Splay(Get_Kth(root,l-1+1),0); //l的前驱l-1伸展到根
    Splay(Get_Kth(root,r+1+1),root); //r的后继r+1伸展到根的右孩子
    Update_Add(Key_value,val);
    Pushup(ch[root][1]);
    Pushup(root);
}
void Move(int l,int r,int p) //截取[l,r]放到位置p之后
{
    Splay(Get_Kth(root,l-1+1),0); //l的前驱l-1伸展到根
    Splay(Get_Kth(root,r+1+1),root); //r的后继r+1伸展到根的右孩子
    int tmp=Key_value; //Key_value=ch[ch[root][1]][0]所统领的子树即[l,r]
    Key_value=0; //剥离[l,r]子树
    Pushup(ch[root][1]); Pushup(root);
    Splay(Get_Kth(root,p+0+1),0); //p伸展到根
    Splay(Get_Kth(root,p+1+1),root); //p的后继p+1伸展到根的右孩子
    Key_value=tmp; par[Key_value]=ch[root][1]; //接上[l,r]子树
    Pushup(ch[root][1]); Pushup(root);
}
void Reverse(int l,int r) //反转[l,r]区间
{
    Splay(Get_Kth(root,l-1+1),0);
    Splay(Get_Kth(root,r+1+1),root);
    Update_Rev(Key_value);
    Pushup(ch[root][1]);
    Pushup(root);
}
void Revolve(int l,int r,int T)
{
    int D=r-l+1;
    int L=T%D;
    if(L==0) return;
    Move(r-L+1,r,l-1);
}
void Insert(int p,int k)
{
    Splay(Get_Kth(root,p+0+1),0); //p伸展到根
    Splay(Get_Kth(root,p+1+1),root); //p的后继p+1伸展到根的右孩子
    printf("Key_value=%d\n",Key_value);
    Inorder(Key_value);
    NewNode(Key_value,ch[root][1],k);
    Pushup(ch[root][1]); Pushup(root);
}
void Collect(int x) //回收节点x统领的子树
{
    if(x==0) return;
    pool[poolsize++]=x;
    Collect(ch[x][0]);
    Collect(ch[x][1]);
}
void Delete(int p)
{
    Splay(Get_Kth(root,p-1+1),0); //p伸展到根
    Splay(Get_Kth(root,p+1+1),root); //p的后继p+1伸展到根的右孩子
    Collect(Key_value);
    par[Key_value]=0;
    Key_value=0;
    Pushup(ch[root][1]); Pushup(root);
}
int Min(int l,int r)
{
    Splay(Get_Kth(root,l-1+1),0);
    Splay(Get_Kth(root,r+1+1),root);
    return mini[Key_value];
}
/******************************** splay - ed ********************************/

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    Init();

    scanf("%d",&m);
    char op[50];
    for(int i=1;i<=m;i++)
    {
        scanf("%s",op);
        if(op[0]==‘A‘)
        {
            int x,y,D; scanf("%d%d%d",&x,&y,&D);
            Add(x,y,D);
        }
        if(op[0]==‘R‘ && op[3]==‘E‘)
        {
            int x,y; scanf("%d%d",&x,&y);
            Reverse(x,y);
        }
        if(op[0]==‘R‘ && op[3]==‘O‘)
        {
            int x,y,T; scanf("%d%d%d",&x,&y,&T);
            Revolve(x,y,T);
        }
        if(op[0]==‘I‘)
        {
            int x,p; scanf("%d%d",&x,&p);
            Insert(x,p);
        }
        if(op[0]==‘D‘)
        {
            int x; scanf("%d",&x);
            Delete(x);
        }
        if(op[0]==‘M‘)
        {
            int x,y; scanf("%d%d",&x,&y);
            printf("%d\n",Min(x,y));
        }
        //Inorder(root);
    }
}

原文地址:https://www.cnblogs.com/dilthey/p/9404613.html

时间: 2024-08-25 21:27:38

POJ 3580 - SuperMemo - [伸展树splay]的相关文章

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

伸展树Splay

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

POJ 3580 SuperMemo

裸Splay区间操作: 内存池+区间加减+区间翻转+插入+删除+维护最值 SuperMemo Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 8552   Accepted: 2801 Case Time Limit: 2000MS Description Your friend, Jackson is invited to a TV show called SuperMemo in which the participa

树-伸展树(Splay Tree)

伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二叉查找树,即它具有和二叉查找树一样的性质:假设x为树中的任意一个结点,x节点包含关键字key,节点x的key值记为key[x].如果y是x的左子树中的一个结点,则key[y] <= key[x]:如果y是x的右子树的一个结点,则key[y] >= key[x]. (02) 除了拥有二叉查找树的性质

# 伸展树 Splay

伸展树 Splay 维基百科上称为伸展树,但是国内好像一般叫平衡树,是众多平衡树中比较优秀的一种. 平衡树左旋右旋不会影响中序遍历顺序. 一棵平衡树的中序遍历顺序是值递增排序的,相当于从小到大到大排了一次序. 平衡树的作用: 平衡树其实就是一棵二叉搜索树,set和map都是平衡树实现. 一棵二叉搜索树理论深度是\(O(log(n))\),但是当二叉树退化成链表的时候,深度就变成了\(O(n)\),很多\(O(log)\)级别从操作会退化成\(O(n)\)线性级别的操作.平衡树就是在不改变二叉搜索

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 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 tree)

伸展树同样是一种平衡二叉搜索树,它的优势在于,在足够长的序列中能保证分摊意义上的高效率,同时也无需记录高度或者平衡因子等信息. 伸展树的高效前提是局部性:刚刚被访问到的数据,可能在短时间内被再次访问:将被访问的下一元素,可能就在不久之前刚刚访问过的元素的附近.因此,伸展树的策略,就是把刚刚访问到的节点,及时"伸展"到树根附近. 所谓"伸展"操作,其实就是BST中的旋转操作.如果每次经过适当旋转,将访问的节点提升一层,直到上升到树根,称为逐层伸展.可以验证,这种伸展方

【BBST 之伸展树 (Splay Tree)】

最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codeforces.com/problemset/problem/675/D,运行效果居然还挺好的,时间快了大概10%,内存少了大概30%. 1 #include <cstdio> 2 #include <cstring> 3 #include <string> 4 #include