zoukankan      html  css  js  c++  java
  • 洛谷 P1377 [TJOI2011]树的序 解题报告

    P1377 [TJOI2011]树的序

    题目描述

    众所周知,二叉查找树的形态和键值的插入顺序密切相关。准确的讲:1、空树中加入一个键值(k),则变为只有一个结点的二叉查找树,此结点的键值即为(k);2、在非空树中插入一个键值(k),若(k)小于其根的键值,则在其左子树中插入(k),否则在其右子树中插入(k)

    我们将一棵二叉查找树的键值插入序列称为树的生成序列,现给出一个生成序列,求与其生成同样二叉查找树的所有生成序列中字典序最小的那个,其中,字典序关系是指对两个长度同为(n)的生成序列,先比较第一个插入键值,再比较第二个,依此类推。

    输入输出格式

    输入格式:

    第一行,一个整数,(n),表示二叉查找树的结点个数。第二行,有(n)个正整数,(k_1)(k_n),表示生成序列,简单起见,(k_1)~(k_n)为一个1到(n)的排列。

    输出格式:

    一行,(n)个正整数,为能够生成同样二叉查找数的所有生成序列中最小的。

    说明

    对于20%的数据,n ≤ 10。

    对于50%的数据,n ≤ 100。

    对于100%的数据,n ≤ 100,000。


    先化简一下模型,我们把(BST)树建好,然后输出中序遍历即是答案。

    然而,直接建(BST)是很容易退化成链的。

    做过题目多的人可能听说过,这个叫笛卡尔树

    笛卡尔树是一种既满足堆性质又满足二叉排序树性质的树。

    方法一:普通笛卡尔树的建树方法

    现在有一个序列按二叉排序树的关键字(Key)从小到大有序,序列中包含小跟堆的关键字(Index)关键字,在(O(N))的时间建树。

    按原数列顺序一个一个将节点加入笛卡尔树,可以确定一定是从链的右边向下找到一个(Index)大于这个点的节点,把这个节点位置占据,然后置让Ta当自己的左儿子即可。

    用栈把最右边的链存下来,栈顶为右边最底的那个点,加入时边弹出边向上找,更新好位置关系后把自己存进栈即可。

    code:

    #include <cstdio>
    #include <algorithm>
    #define ls t[now].ch[0]
    #define rs t[now].ch[1]
    #define f t[now].par
    const int N=100010;
    struct node
    {
        int dat,index;
        bool friend operator <(node n1,node n2)
        {
            return n1.dat<n2.dat;
        }
    }a[N];
    struct BST
    {
        int ch[2],dat,index,par;//左右儿子,BST域,堆域,父亲
    }t[N];
    int tot=0,n,s[N];
    void connect(int fa,int now,int typ)
    {
        f=fa;
        t[fa].ch[typ]=now;
    }
    void dfs(int now)
    {
        if(!now) return;
        printf("%d ",t[now].dat);
        dfs(ls);
        dfs(rs);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            a[i].index=i;
        }
        std::sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)
        {
            int last=0;
            while(tot&&t[s[tot]].index>a[i].index)
                last=tot--;
            t[i].dat=a[i].dat;
            t[i].index=a[i].index;
            connect(s[tot],i,1);
            connect(i,s[last],0);
            s[++tot]=i;
        }
        dfs(t[0].ch[1]);
        return 0;
    }
    
    

    方法二:按堆性质自底向上建立。
    这个方法好厉害,真的很强。

    则如果两个点的(Key)在“当前”数值上的差最小,那么Ta俩一定有一条边。

    (Index)从大到小建立笛卡尔树,则对(1)-(Index-1)的点中,是不会有(Index)的儿子的。

    (pre[i])(suc[i])分别存储(Key)(i)的点在“当前”所相邻的前驱和后继。

    每次处理完一个点,更新与它相连的“前驱”和“后继”,这对应了“当前”

    注意到原数据为1到n的排列,可以用桶排,比较快。

    Code:

    #include <cstdio>
    const int N=100010;
    int a[N],b[N],L[N],R[N],pre[N],suc[N],n;
    void dfs(int now)
    {
        if(!now) return;
        printf("%d ",a[now]);
        dfs(L[now]);dfs(R[now]);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            b[a[i]]=i;
            pre[i]=i-1,suc[i]=i+1;
        }
        for(int i=n;i>1;i--)
        {
            int pree=pre[a[i]],succ=suc[a[i]];
            if(b[pree]>b[succ])
                R[b[pree]]=i;
            else
                L[b[succ]]=i;
            suc[pree]=succ;pre[succ]=pree;
        }
        dfs(1);
        return 0;
    }
    
    

    2018.6.19

  • 相关阅读:
    Unity The Method Signature Matching Rule
    Unity The Property Matching Rule
    Unity The Type Matching Rule
    Unity The Custom Attribute Matching Rule
    Unity The Member Name Matching Rule
    Unity No Policies
    Unity The Return Type Matching Rule
    Unity The Parameter Type Matching Rule
    Unity The Namespace Matching Rule
    关于TSQL递归查询的(转)
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9199591.html
Copyright © 2011-2022 走看看