【NOIP模拟考三】线段树/ST表 day2 second 二叉树

来源:互联网 发布:网络歌曲女生唱的 编辑:程序博客网 时间:2024/05/22 01:53

题目描述

 给定一棵二叉树,节点标号从1到n。在不改变其中序遍历的情况下,请改变树的结构,使得这棵二叉树的先序遍历(前序遍历)字典序最小。

输入

第一行一个整数n,表示二叉树的节点数。
接下来n行,每行两个整数。第i行的两个整数表示编号为i的节点的左儿子和右儿子的编号(不存在即为0)。

输出

输出一行n个整数,表示不改变中序遍历的情况下字典序最小的前序遍历序列。

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

55 40 02 10 00 0

样例输出

1 2 3 5 4

提示


1     3        N/A


2     4        N/A


3    10        N/A


4    100       树为一条链,且只存在右儿子关系。


5    1000      给出的树满足排序二叉树的性质。即任意一个节点


6    100000    左子树中所有值<该节点<右子树中所有值。


7    65535     满二叉树


8    100000    N/A


9    100000    N/A



    看那水题一道两道三道连成线!

    咳咳,我还是要填坑的。。。

    考试时这道题还是想了很久,想过各种诡异的方法,比如LCA啦、平衡树啦、暴力啊。。。什么都有,后来才恍然大悟——这题好水。。。

    我们要让中序遍历的顺序不变,却不知道中序遍历的结果是什么,怎么可以呢,是吧……所以第一步应该中序遍历!

    我们就举样例的例子吧!


    好久没有看到图片了,是不是很激动?
    很容易发现,我们只需要让根最小就是最优的,那么我们很显然要1当根。其次,显然的,根的左儿子最小就最好,于是我们把2当做1的左儿子,但是,尽管样例是可以的,却并不代表所有数据都可以,别忘了中序遍历的限制!
    很容易发现,只要是中序遍历根左边的,就可以当成是左儿子,那么我们可以在根左边的中选出一个最小的来当根的左儿子,然后同样的在右边选一个最小的当右儿子,用线段树维护区间最小值就好了啦……(当然ST表也是可以的……)
    所以样例就很容易解释了——先选1,在左边选择最小的2,2没有左边,于是在右边选择最小的3,3也没有左边,于是在右边选择最小的5,这时1的左子树完毕,在右边选择最小的4,整棵树完毕。按遍历顺序依次输出:
    1 2 3 5 4
    PS:这道题虽然是求先序遍历,其实完全不需要再建一棵树,只要假想有那么一棵树就可以了……
    好了,一如既往的代码……
#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 100005int n,ls[N],rs[N],fa[N],r;int mid[N],wz[N],tim;void dfs(int r){    if(ls[r])        dfs(ls[r]);    mid[++tim]=r;    wz[r]=tim;    if(rs[r])        dfs(rs[r]);}struct ST{    int l,r,minn;}s[4*N-15];void make(int l,int r,int q){    s[q].l=l;s[q].r=r;    if(l<r)    {        int mid=(l+r)>>1;        make(l,mid,q<<1);        make(mid+1,r,q<<1|1);    }}void insert(int a,int k,int q){    if(s[q].l==k&&s[q].r==k)    {        s[q].minn=a;        return;    }    if(k>((s[q].l+s[q].r)>>1))        insert(a,k,q<<1|1);    else        insert(a,k,q<<1);    s[q].minn=min(s[q<<1].minn,s[q<<1|1].minn);}int minn(int l,int r,int q){    if(l==s[q].l&&r==s[q].r)        return s[q].minn;    int mid=(s[q].l+s[q].r)>>1;    if(r<=mid)        return minn(l,r,q<<1);    if(l>mid)        return minn(l,r,q<<1|1);    return min(minn(l,mid,q<<1),minn(mid+1,r,q<<1|1));}void getans(int l,int r){    if(l>r)        return;    int now=minn(l,r,1);    int p=wz[now];    printf(" %d",now);    getans(l,p-1);    getans(p+1,r);}int main(){    //freopen("bitree.in","r",stdin);    //freopen("bitree.out","w",stdout);    scanf("%d",&n);    make(1,n,1);    for(int i=1;i<=n;i++)    {        scanf("%d%d",&ls[i],&rs[i]);        if(ls[i])            fa[ls[i]]=i;        if(rs[i])            fa[rs[i]]=i;    }    for(int i=1;i<=n;i++)        if(!fa[i])        {            r=i;            break;        }    dfs(r);    for(int i=1;i<=n;i++)        insert(mid[i],i,1);    printf("1");    getans(1,wz[1]-1);    getans(wz[1]+1,n);}