• UVA 11922 Permutation Transformer Splay


    Description

    Download as PDF


      Permutation Transformer 

    Write a program to transform the permutation 1, 2, 3,..., n according to m instructions. Each instruction (ab) means to take out the subsequence from the a-th to the b-th element, reverse it, then append it to the end.

    Input

    There is only one case for this problem. The first line contains two integers n and m ( 1$ le$nm$ le$100, 000). Each of the next m lines contains an instruction consisting of two integers a and b ( 1$ le$a$ le$b$ le$n).

    Output

    Print n lines, one for each integer, the final permutation.


    Explanation of the sample below

    Instruction (2,5): Take out the subsequence {2,3,4,5}, reverse it to {5,4,3,2}, append it to the remaining permutation {1,6,7,8,9,10}

    Instruction (4,8): The subsequence from the 4-th to the 8-th element of {1,6,7,8,9,10,5,4,3,2} is {8,9,10,5,4}. Take it out, reverse it, and you'll get the sample output.


    Warning: Don't use cincout for this problem, use faster i/o methods e.g scanfprintf.

    Sample Input

    10 2
    2 5
    4 8
    

    Sample Output

    1
    6
    7
    3
    2
    4
    5
    10
    9
    8
    




    -----------------

    对区间[1,n]执行m条指令。

    指令[a,b]表示取出第a~b元素翻转并放入队列尾部。

    Splay的构造参考《运用伸展树解决数列维护问题》

    具体代码的编写参考了NOI 2005 维护数列-BYVoid

    -----------------

    /**
        Splay Tree 索引
        Node:
            void addIt(int ad) 区间添加ad
            void revIt() 区间翻转
            void upd() 更新结点,子树改变后使用
            void pushdown() 向下传递懒惰标记
        Splay:
            Node* newNode(int v,Node* f) 构造一个val值为v的节点,父节点为f,
            Node* build(int l,int r,Node* f) 构造区间[l,r],父节点为f;
            void rotate(Node* t,int d) 左旋右旋
            void splay(Node* t,Node* f) 将结点t伸展到f
            void select(int k) 返回第k个节点并伸展到f,不计虚拟结点
            Node*&get(int l, int r) 返回区间[l,r],即l-1旋转到根,r+1旋转到根的右儿子
            void reverse(int l,int r) 翻转区间[l,r]
            void split(int l,int r,Node*&s1) 将区间[l,r]剪切到s1
            void cut(int l,int r) 将区间[l,r]剪切到序列尾部
            void init(int n) 构造区间[1,n]并初始化
            void show(Node* rt) 输出树rt的中序遍历,debug用
            void output(int l,int r) 输出并伸展区间[l,r],复杂度较高待优化
    **/
    #include <iostream>
    #include <ctime>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    const int MAX_N = 150000 + 10;
    const int INF = ~0U >> 1;
    struct Node{
        Node *ch[2],*pre;//左右子树,父节点
        int val;//关键字
        int size;//以它为根的子树的总结点数
        int mx;//最大值
        int add;//添加标记
        bool rev;//翻转标记
        Node(){
            size=0;
            val=mx=-INF;
            add=0;
        }
        void addIt(int ad){
            add+=ad;
            mx+=ad;
            val+=ad;
        }
        void revIt(){
            rev^=1;
        }
        void upd(){
            size=ch[0]->size+ch[1]->size+1;
            mx=max(val,max(ch[0]->mx,ch[1]->mx));
        }
        void pushdown();
    }Tnull,*null=&Tnull;
    void Node::pushdown(){
        if (add!=0){
            for (int i=0;i<2;++i)
                if (ch[i]!=null) ch[i]->addIt(add);
            add = 0;
        }
        if (rev){
            swap(ch[0],ch[1]);
            for (int i=0;i<2;i++)
                if (ch[i]!=null) ch[i]->revIt();
            rev = 0;
        }
    }
    struct Splay{
        Node nodePool[MAX_N],*cur;//内存分配
        Node* root;//根
        Splay(){
            cur=nodePool;
            root=null;
        }
        //清空内存,init()调用
        void clear(){
            cur=nodePool;
            root=null;
        }
        //新建节点,build()用
        Node* newNode(int v,Node* f){
            cur->ch[0]=cur->ch[1]=null;
            cur->size=1;
            cur->val=v;
            cur->mx=v;
            cur->add=0;
            cur->rev=0;
            cur->pre=f;
            return cur++;
        }
        //构造区间[l,r]中点m,init()使用
        Node* build(int l,int r,Node* f){
            if(l>r) return null;
            int m=(l+r)>>1;
            Node* t=newNode(m,f);
            t->ch[0]=build(l,m-1,t);
            t->ch[1]=build(m+1,r,t);
            t->upd();
            return t;
        }
        //旋转操作,c=0表示左旋,c=1表示右旋
        void rotate(Node* x,int c){
            Node* y=x->pre;
            y->pushdown();
            x->pushdown();
            //先将y结点的标记向下传递(因为y在上面)
            y->ch[!c]=x->ch[c];
            if (x->ch[c]!=null) x->ch[c]->pre=y;
            x->pre=y->pre;
            if (y->pre!=null)
            {
                if (y->pre->ch[0]==y) y->pre->ch[0]=x;
                else y->pre->ch[1]=x;
            }
            x->ch[c]=y;
            y->pre=x;
            y->upd();//维护y结点
            if (y==root) root=x;
        }
        //Splay操作,表示把结点x转到结点f的下面
        void splay(Node* x,Node* f){
            x->pushdown();//下传x的标记
            while (x->pre!=f){
                if (x->pre->pre==f){//父节点的父亲为f,执行单旋
                    if (x->pre->ch[0]==x) rotate(x,1);
                    else rotate(x,0);
                }else{
                    Node *y=x->pre,*z=y->pre;
                    if (z->ch[0]==y){
                        if (y->ch[0]==x) rotate(y,1),rotate(x,1);//一字型旋转
                        else rotate(x,0),rotate(x,1);//之字形旋转
                    }else{
                        if (y->ch[1]==x) rotate(y,0),rotate(x,0);//一字型旋转
                        else rotate(x,1),rotate(x,0);//之字形旋转
                    }
                }
            }
            x->upd();//最后再维护X结点
        }
        //找到处在中序遍历第k个结点,并将其旋转到结点f的下面
        void select(int k,Node* f){
            int tmp;
            Node* x=root;
            x->pushdown();
            k++;//空出虚拟节点
            for(;;){
                x->pushdown();
                tmp=x->ch[0]->size;
                if (k==tmp+1) break;
                if (k<=tmp) x=x->ch[0];
                else{
                    k-=tmp+1;
                    x=x->ch[1];
                }
            }
            splay(x,f);
        }
        //选择[l,r]
        Node*&get(int l, int r){
            select(l-1,null);
            select(r+1,root);
            return root->ch[1]->ch[0];
        }
        //翻转[l,r]
        void reverse(int l,int r){
            Node* o=get(l,r);
            o->rev^=1;
            splay(o,null);
        }
        //剪切出[l,r]到s1
        void split(int l,int r,Node*&s1)
        {
            Node* tmp=get(l,r);
            root->ch[1]->ch[0]=null;
            root->ch[1]->upd();
            root->upd();
            s1=tmp;
        }
        void cut(int l,int r)
        {
            Node* tmp;
            split(l,r,tmp);
            select(root->size-1,null);
            select(root->size-2,root);
            root->ch[0]->ch[1]=tmp;
            tmp->pre=root->ch[0];
            root->ch[0]->upd();
            root->upd();
        }
        //初始化
        void init(int n){
            clear();
            root=newNode(0,null);
            root->ch[1]=newNode(n+1,root);
            root->ch[1]->ch[0]=build(1,n,root->ch[1]);
            root->upd();
        }
        //输出中序遍历,debug用
        void show(Node* rt){
            if (rt==null) return;
            if (rt->ch[0]!=null) show(rt->ch[0]);
            cerr<<"rt="<<rt->val;
            if (rt->ch[0]!=null) cerr<<" l="<<rt->ch[0]->val;
            if (rt->ch[1]!=null) cerr<<" r="<<rt->ch[1]->val;
            if (rt->pre  !=null) cerr<<" pre="<<rt->pre->val;
            cerr<<endl;
            if (rt->ch[1]!=null) show(rt->ch[1]);
        }
        //按序输出
        void output(int l,int r){
            for (int i=l;i<=r;i++){
                select(i,null);
                cout<<root->val<<endl;
            }
            //cout<<endl;
        }
    
    }T;
    int main()
    {
        int n,m,a,b;
        while (~scanf("%d%d",&n,&m))
        {
            T.init(n);
            while (m--)
            {
                scanf("%d%d",&a,&b);
                if (b<a) swap(a,b);
                T.reverse(a,b);
                T.cut(a,b);
            }
            T.output(1,n);
        }
        return 0;
    }
    




  • 相关阅读:
    Visual Studio 2005 不能调试的问题
    自学C语言_第一章
    批处理For循环一键Update补丁程序
    小米MiFlash刷机报错售后方法参考
    Civil 3D 2012 CAD安装完成后打开报错“致命错误:Unhandled Delayload "D3DCOMPILER_47.dll"
    Windows查看端口被占用
    vc ++6.0打开或者添加出现错误解决方案
    一天总结
    一天总结
    一天总结
  • 原文地址:https://www.cnblogs.com/cyendra/p/3681593.html
Copyright © 2020-2023  润新知