zoukankan      html  css  js  c++  java
  • POJ 3580

    题目链接: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代码:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn=2e5+10;
    int n,m;
    int a[maxn];
    
    /******************************** splay - st ********************************/
    #define Key_value ch[ch[root][1]][0]
    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("
    ");
    }
    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)
    {
        while(par[x]!=goal)
        {
            if(par[par[x]]==goal) 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伸展到根的右孩子
        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的前驱p-1伸展到根
        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
    ",Min(x,y));
            }
            //Inorder(root);
        }
    }

    两组数据:

    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
    10
    1 2 3 4 5 6 7 8 9 10
    15
    ADD 4 8 3
    MIN 5 7
    MIN 7 10
    REVERSE 2 5
    MIN 2 6
    MIN 2 3
    INSERT 3 4
    MIN 3 4
    MIN 5 10
    DELETE 6
    MIN 3 5
    MIN 4 4
    REVOLVE 3 6 7
    MIN 5 8
    MIN 7 10

    第一次写完之后一些BUG通过第一组数据调完了,感觉应该没问题了,然后交了发现WA,

    然后用第二组数据一测,发现居然是因为用来debug的一些输出忘记删掉了……尴尬……

  • 相关阅读:
    IDEA快捷键收集
    Jmeter录制HTTPS
    Jmeter 线程组、运行次数参数化
    fiddler 抓取iphone发出的http和https包
    Appium 点击屏幕
    安卓程序如何保证低内存下依然存在
    listview
    数据库操作
    数据存储
    测试
  • 原文地址:https://www.cnblogs.com/dilthey/p/9404613.html
Copyright © 2011-2022 走看看