POJ 3580 SuperMemo (Splay 区间更新、翻转、循环右移,插入,删除,查询)

SuperMemo

Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 13917   Accepted: 4352
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. 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题意:给你一个序列以及许多操作,包括区间更新、翻转、循环右移,插入,删除,查询分析:都是Splay的一些基本的操作,更新和翻转使用lazy标记的思想就行了,对于循环右移,首先找出左端点移动的位置k,然后就是将[k,r]区间移动到[l,k-1]的前面去,我们可以先定位出[k,r],再调整出[l-1,l],将[k,r]接上去就行了。插入操作也是通过变换找到这个子树,再新加节点就是了。
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;

#define Key_value ch[ch[root][1]][0]
const int MAXN = 200000+100;
const int INF = 0X3F3F3F3F;
int fa[MAXN],ch[MAXN][2],key[MAXN],sz[MAXN],add[MAXN],rev[MAXN],mi[MAXN];
int root,tot1;
int s[MAXN],tot2;//内存池、内存池容量
int a[MAXN];
int n,m;

void NewNode(int &r,int pre,int k)
{
    if(tot2) r=s[tot2--];
    else r=++tot1;
    ch[r][0]=ch[r][1]=0;
    fa[r]=pre;
    sz[r]=1;
    add[r]=rev[r]=0;
    key[r]=mi[r]=k;
}
void Update_Rev(int r)
{
    if(r==0) return;
    swap(ch[r][0],ch[r][1]);
    rev[r]^=1;
}
void Update_Add(int r,int val)
{
    if(r==0) return;
    add[r]+=val;
    key[r]+=val;
    mi[r]+=val;
}
void Push_Up(int r)
{
    sz[r]=sz[ch[r][0]]+sz[ch[r][1]]+1;
    mi[r]=key[r];
    if(ch[r][0]) mi[r]=min(mi[r],mi[ch[r][0]]);
    if(ch[r][1]) mi[r]=min(mi[r],mi[ch[r][1]]);
}
void Push_Down(int r)
{
    if(rev[r])
    {
        Update_Rev(ch[r][0]);
        Update_Rev(ch[r][1]);
        rev[r]=0;
    }
    if(add[r])
    {
        Update_Add(ch[r][0],add[r]);
        Update_Add(ch[r][1],add[r]);
        add[r]=0;
    }
}
void Build(int &x,int l,int r,int pre)
{
    if(l>r) return;
    int mid=(l+r)/2;
    NewNode(x,pre,a[mid]);
    Build(ch[x][0],l,mid-1,x);
    Build(ch[x][1],mid+1,r,x);
    Push_Up(x);
}
void Init()
{
    root=tot1=tot2=0;
    ch[root][0]=ch[root][1]=sz[root]=add[root]=rev[root]=fa[root]=0;
    mi[root]=INF;
    NewNode(root,0,INF);
    NewNode(ch[root][1],root,INF);
    Build(Key_value,1,n,ch[root][1]);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void Rotate(int x,int d)
{
    int y=fa[x];
    Push_Down(y);
    Push_Down(x);
    ch[y][!d]=ch[x][d];
    fa[ch[x][d]]=y;
    if(fa[y]) ch[fa[y]][ch[fa[y]][1]==y]=x;
    fa[x]=fa[y];
    ch[x][d]=y;
    fa[y]=x;
    Push_Up(y);
}
//伸展操作,将r调整到goal下面
void Splay(int r,int goal)
{
    Push_Down(r);
    while(fa[r]!=goal)
    {
        if(fa[fa[r]]==goal)
        {
            //这题有反转操作,需要先push_down,在判断左右孩子
            Push_Down(fa[r]);
            Push_Down(r);
            Rotate(r,ch[fa[r]][0]==r);
        }
        else
        {
            //这题有反转操作,需要先push_down,在判断左右孩子
            Push_Down(fa[fa[r]]);
            Push_Down(fa[r]);
            Push_Down(r);
            int y=fa[r];
            int d=(ch[fa[y]][0]==y);
            //两个方向不同,则先左旋再右旋
            if(ch[y][d]==r)
            {
                Rotate(r,!d);
                Rotate(r,d);
            }
            //两个方向相同,相同方向连续两次
            else
            {
                Rotate(y,d);
                Rotate(r,d);
            }
        }
    }
    Push_Up(r);
    if(goal==0) root=r;
}
int Get_Kth(int r,int k)
{
    Push_Down(r);
    int t=sz[ch[r][0]]+1;
    if(t==k)return r;
    if(t>k)return Get_Kth(ch[r][0],k);
    else return Get_Kth(ch[r][1],k-t);
}
int Get_Min(int r)
{
    Push_Down(r);
    while(ch[r][0])
    {
        r=ch[r][0];
        Push_Down(r);
    }
    return r;
}
void Erase(int r)
{
    if(r)
    {
        s[++tot2]=r;
        Erase(ch[r][0]);
        Erase(ch[r][1]);
    }
}
int Get_Max(int r)
{
    Push_Down(r);
    while(ch[r][1])
    {
        r=ch[r][1];
        Push_Down(r);
    }
    return r;
}
void update_add(int l,int r,int val)
{
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,r+2),root);
    Update_Add(Key_value,val);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void update_rev(int l,int r)
{
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,r+2),root);
    Update_Rev(Key_value);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void update_reve(int l,int r,int T)
{
    int len=r-l+1;
    T=(T%len+len)%len;
    if(T==0) return;
    int k=r-T+1;
    Splay(Get_Kth(root,k),0);
    Splay(Get_Kth(root,r+2),root);
    int tmp=Key_value;
    Key_value=0;
    Push_Up(ch[root][1]);
    Push_Up(root);
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,l+1),root);
    Key_value=tmp;
    fa[Key_value]=ch[root][1];
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void update_int(int x,int P)
{
    Splay(Get_Kth(root,x+1),0);
    Splay(Get_Kth(root,x+2),root);
    NewNode(Key_value,ch[root][1],P);
    Push_Up(ch[root][1]);
    Push_Up(root);
}
void update_del(int x)
{
    Splay(Get_Kth(root,x),0);
    Splay(Get_Kth(root,x+2),root);
    Erase(Key_value);
    fa[Key_value]=0;
    Key_value=0;
    Push_Up(ch[root][1]);
    Push_Up(root);
}
int query_min(int l,int r)
{
    Splay(Get_Kth(root,l),0);
    Splay(Get_Kth(root,r+2),root);
    return mi[Key_value];
}

char str[20];
int x,y,D,P,T;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    Init();
    scanf("%d",&m);
    while(m--)
    {
        scanf("%s",str);
        if(strcmp(str,"ADD")==0)
        {
            scanf("%d%d%d",&x,&y,&D);
            update_add(x,y,D);
        }
        else if(strcmp(str,"REVERSE")==0)
        {
            scanf("%d%d",&x,&y);
            update_rev(x,y);
        }
        else if(strcmp(str,"REVOLVE")==0)
        {
            scanf("%d%d%d",&x,&y,&T);
            update_reve(x,y,T);
        }
        else if(strcmp(str,"INSERT")==0)
        {
            scanf("%d%d",&x,&P);
            update_int(x,P);
        }
        else if(strcmp(str,"DELETE")==0)
        {
            scanf("%d",&x);
            update_del(x);
        }
        else
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",query_min(x,y));
        }
    }
    return 0;
}
 
时间: 2024-10-07 05:06:22

POJ 3580 SuperMemo (Splay 区间更新、翻转、循环右移,插入,删除,查询)的相关文章

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 .

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区间操作: 内存池+区间加减+区间翻转+插入+删除+维护最值 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

POJ 3225 (线段树 区间更新) Help with Intervals

这道题搞了好久,其实坑点挺多.. 网上找了许多题解,发现思路其实都差不多,所以就不在重复了. 推荐一篇比较好的题解,请戳这. 另外,如果因为可能要更新多次,但最终查询只需要一次,所以没有写pushup函数,仅有一个pushdown. 1 #include <cstdio> 2 3 const int maxn = 131072; 4 //const int maxn = 10; 5 int qL, qR, op; 6 int setv[maxn << 2], xorv[maxn &

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, {A1, A2, ... An}. Then the h

Splay树(多操作)——POJ 3580 SuperMemo

对应POJ题目:点击打开链接 SuperMemo Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 11309   Accepted: 3545 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

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的左子树

又一道Splay吐血题 [POJ 3580] SuperMemo

SuperMemo Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 9878   Accepted: 3177 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.

POJ 3580 SuperMemo 题解

题目大意:维护一个序列,支持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 插入P到第x个数后面 5.DELETE x 删除第x个数 6.MIN x y 查询第x个数到第y个数之间最小值 ADD和REVERSE打标记然后及时下传就可以..REVOLVE的T可能为负还可能很大,要注意取模.REVOLVE可以通过3个REVERSE操作完成,也可以