zoukankan      html  css  js  c++  java
  • HDU 1890(splay tree 区间翻转)

    前后折腾了一天半时间才搞定。。从学习lazy到理解代码。。—_—||

    题意是说每次把第i大的数所在位置和第i个位置之间翻转,输出每个数在翻转前的位置。

    首先我们要想到,在splay tree 中,对于根节点来说,左子树的大小+1就是它在数组中的位置(从1开始标号),左子树的各元素也是在数列中位于根结点左边的

    我们现在用splay tree来维护未排序的序列,那对于题目要求的翻转操作来说,对象就仅仅是根节点的左子树了

    我们要做的事情可以抽象成这样:每次把第i大的结点旋转至根,再把根节点的左子树打上rev标记,输出答案i+s[ch[root][0]],然后删除根节点

    对于答案的理解,可以分为两部分,首先i是至在数列左边已排序的序列(包括待排序的根节点),s[ch[root][0]]则是在目前操作的结点左边的节点数

    对于lazy标记的理解建议最好画画图

    现在要解决的问题有两个:1、如何找到第i大的结点 2.确定该结点在splay tree中的位置

    对于第一个问题,我们把数列先排序(记得把序列的id同时捆绑),那我们只需要依次对这n个数依次处理就好

    第二个问题要稍微难理解一些。这里也稍稍用了离散化的思想。我们把数列按照要求排序后,按照id(原序列次序)相当于离散成1~n这n个数。

    我们建立一棵结点编号为1~n的平衡树作为初始状态

    则不难理解这棵树维护的是排序前每个数的id,也就是说维护的是原序列

    其次要先理解splay tree中结点编号和该结点在原数列的下标的关系——相等。

    举个例子:现在要操作第3个结点,他的id是5,也就是说这是第3大的结点,在原序列中在第5个位置。

         那我把5号结点旋转到根,看看左子树的大小(相当于看看原序列中5号位置左边还剩几个元素,因为第1第2大的都删了)

    代码是参考网上的。

    #include"cstdio"
    #include"queue"
    #include"cmath"
    #include"stack"
    #include"iostream"
    #include"algorithm"
    #include"cstring"
    #include"queue"
    #include"map"
    #include"vector"
    #define ll long long
    #define mems(a,b) memset(a,b,sizeof(a))
    
    using namespace std;
    const int MAXN = 1e5+50;
    const int MAXE = 200500;
    const int INF = 0x3f3f3f;
    
    int pre[MAXN],ch[MAXN][2],rev[MAXN],s[MAXN];
    int root,n;
    
    struct Node{
        int w,id;
    }node[MAXN];
    
    bool cmp(Node a,Node b){
        if(a.w==b.w) return a.id<b.id;
        return a.w<b.w;
    }
    
    void newnode(int &pos,int fa,int k){
        pos=k;
        pre[pos]=fa;
        ch[pos][0]=ch[pos][1]=0;
        rev[pos]=0;
        s[pos]=1;
    }
    
    void pushdown(int x){
        if(rev[x]){
            rev[ch[x][0]]^=1;
            rev[ch[x][1]]^=1;
            swap(ch[x][0],ch[x][1]);
            rev[x]=0;
        }
    }
    
    void pushup(int x){
        s[x]=s[ch[x][0]]+s[ch[x][1]]+1;
    }
    
    void build(int &x,int l,int r,int fa){///建立平衡树
        if(l>r) return;
        int mid=(l+r)>>1;
        newnode(x,fa,mid);
        build(ch[x][0],l,mid-1,x);
        build(ch[x][1],mid+1,r,x);
        pushup(x);
    }
    
    void init(){
        root=0;
        sort(node+1,node+1+n,cmp);
        build(root,1,n,0);
    }
    
    void Rotate(int x,int kind){
        int fa=pre[x];
        pushdown(fa);
        pushdown(x);        ///注意先后次序
        ch[fa][!kind]=ch[x][kind];
        pre[ch[x][kind]]=fa;
    
        if(pre[fa]) ch[pre[fa]][ch[pre[fa]][1]==fa]=x;
        pre[x]=pre[fa];
    
        ch[x][kind]=fa;
        pre[fa]=x;
        pushup(fa);         ///注意先后次序
        pushup(x);
    }
    
    void Splay(int r,int goal){
        pushdown(r);
        while(pre[r]!=goal){
            if(pre[pre[r]]==goal){
                pushdown(pre[r]);       ///不可省去,会影响ch[pre[r]][0],下面类似语句同理不可省
                pushdown(r);
                Rotate(r,ch[pre[r]][0]==r);
            }
            else{
                int fa=pre[r];
                pushdown(pre[fa]);      ///先pushdown再求kind -_-|| 因为这个TLE了半天
                pushdown(pre[r]);
                pushdown(r);
                int kind=(ch[pre[fa]][0]==fa);
                if(ch[fa][kind]==r){
                    Rotate(r,!kind);
                    Rotate(r,kind);
                }
                else{
                    Rotate(fa,kind);
                    Rotate(r,kind);
                }
            }
        }
        pushup(r);
        if(!goal) root=r;
    }
    
    int getmax(int x){      ///寻找比x小但是最接近x的数
        pushdown(x);
        while(ch[x][1]){
            x=ch[x][1];
            pushdown(x);
        }
        return x;
    }
    
    void del(int x){
        if(!ch[root][0]){
            root=ch[root][1];
            pre[root]=0;
        }
        else{
            int t=getmax(ch[root][0]);
            Splay(t,root);
            ch[t][1]=ch[root][1];
            pre[ch[root][1]]=t;
            root=t;
            pre[root]=0;
            pushup(root);
        }
    }
    
    int main(){
        //freopen("in.txt","r",stdin);
        while(scanf("%d",&n)&&n){
            for(int i=1;i<=n;i++){
                scanf("%d",&node[i].w);
                node[i].id=i;
            }
            init();
            for(int i=1;i<n;i++){
                Splay(node[i].id,0);
                rev[ch[root][0]]^=1;
                printf("%d ",i+s[ch[root][0]]);
                del(root);
            }
            printf("%d
    ",n);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    STL
    Python
    Swift学习笔记
    Swift学习笔记
    Cocos2d-x -- 如何让背景从上到下滚动
    Cocos2d-x -- 图片菜单按钮
    How to change in the Cocos2d-x project from landscape to portrait both in iOS and Android
    系统集成项目管理工程师和信息系统管理工程师的区别是什么?
    公积金取出来后悔了 公积金取出来好还是不取好?
    青岛公积金贷款额度最高多少?怎么算?
  • 原文地址:https://www.cnblogs.com/luxiaoming/p/5129862.html
Copyright © 2011-2022 走看看