noip 模拟赛(by azui大爷) day2 t2(附O(1)求RMQ)

7 篇文章 0 订阅
1 篇文章 0 订阅
看到第三题是个毒瘤题,于是先来写下第二题的题解。题意:给你一颗二叉树,要求在不改变其中序遍历的前提下,改变树的结构,使新树前序遍历的字典序最小,输出这个前序遍历题解:表示好久都没有复习什么先序后序遍历了,结论什么的都忘干净了于是这道题我直接输的12345……,还骗到20分。这道题如果按照改变树的结构的思路来想的话,其实就是AVL中的zig,zag
摘要由CSDN通过智能技术生成

看到第三题是个毒瘤题,于是先来写下第二题的题解。


题意:给你一颗二叉树,要求在不改变其中序遍历的前提下,改变树的结构,

使新树前序遍历的字典序最小,输出这个前序遍历


题解:

表示好久都没有复习什么先序后序遍历了,结论什么的都忘干净了

于是这道题我直接输的12345……,还骗到20分。


这道题如果按照改变树的结构的思路来想的话,其实就是

AVL中的zig,zag操作,或者splay中的rotate,这样是可以做的


但是更简单的做法是分治,我们只需要在意中序遍历的顺序就可以了

显然我们可以将1作为根节点,那么现在,在中序遍历数组1左边的就

是目前的左子树,右边就是右子树,显然具有子性质,找到最小的编号

作为子树的根就可以了


这里额外需要做的是快速查找区间最小值,显然zkw线段树可以快速的

做到这一点,但是对于静态查询且不强制在线的话,是有nlogn预处理

O(1)查找的做法的:st表。 其实就是一个倍增


st[i][j]表示第j个节点到从第j个节点往后2^i个节点的最小值

(这个i,j位置有点别扭是因为说什么这样在预处理的时候上会快一点,但查询的时候会慢一点,玄学)


那么

st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))])(下面的st数组是存的下标,所以额外写了一个cmp)


查询(s,e)的时候,只需要稍微大于(e-s+1)/2且刚好是2的整数次幂的值

以s为起点取一段,再以e为终点取一段就可以了,不用去处理这两段重合

情况,因为最小值不会被影响


code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
 
const int MAXN=100005;
const int LOG=17;
using namespace std;
struct node{
    int fa,lc,rc;
}h[MAXN];
int n,mcnt,M[MAXN]; bool f;
int lg2[MAXN],st[LOG+5][MAXN];
 
inline void Read(int &Ret){
    char ch;bool flag=0;
    for(;ch=getchar(),!isdigit(ch);)if(ch=='-')flag=1;
    for(Ret=ch-'0';ch=getchar(),isdigit(ch);Ret=Ret*10+ch-'0');
    flag&&(Ret=-Ret);
}
void Get_Middle(int i){
    if(!i) return;
    Get_Middle(h[i].lc);
    M[++mcnt]=i;
    Get_Middle(h[i].rc);
}
#define cmp(i,j) (M[i]<M[j]?i:j)
inline int RMQ(int s,int e){
    int t=lg2[e-s+1];
    return cmp(st[t][s],st[t][e-(1<<t)+1]);
}
int Find_Front(int s,int e){
    if(s>e) return 0;
    int t=RMQ(s,e);
    if(!f) printf("%d",M[t]),f=1;
    else printf(" %d",M[t]);
    Find_Front(s,t-1);
    Find_Front(t+1,e);
    return M[t];
}
 
inline void prepare(){
    for(int i=2;i<=n;i++)
        lg2[i]=lg2[i>>1]+1;
    for(int i=1;i<=n;i++) st[0][i]=i;
    for(int i=1;i<=LOG;i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
            st[i][j]=cmp(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
int main()
{
    Read(n);
    for(int i=1;i<=n;i++)
    {
        Read(h[i].lc); Read(h[i].rc);
        h[h[i].lc].fa=h[h[i].rc].fa=i;
    }
    int r=1; while(h[r].fa) r=h[r].fa;
    Get_Middle(r); prepare();
    Find_Front(1,n); putchar(10);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值