[HZOI2016]简单的Treap——笛卡尔树

好像这题只有这一个OJ有

题目大意:

给定一个序列带权值和优先级,构造一个treap并且输出它的前序遍历(要求 O(n) O ( n ) 构造)。

思路:

要用到一种叫做笛卡尔树的东西(似乎和treap没有什么不同),主要是它很巧妙的构造方法。
按照权值大小排序,一个一个插入,新插入的节点保证要在整棵树的最右边,但是同时要满足小根堆的限制,所以只能插到一半停下来,刚好满足小根堆性质为止,但是要求为二叉搜索树,所以只需要把后面的子树全部变成它的左儿子即可。右边的序列用栈来维护。

/*=======================
 * Author : ylsoi
 * Problem : easy treap
 * Algorithm : ***tree
 * Time : 2018.5.13
 * =====================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<stack>
using namespace std;
void File(){
    freopen("treap.in","r",stdin);
    freopen("treap.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long 
#define inf INT_MAX
const int maxn=5e5+10;
int n,va[maxn],w[maxn],qu[maxn];
bool cmp(int x,int y){return va[x]<va[y];}
int ch[maxn][2],fa[maxn],root;
stack<int>stac;
void insert(int num){
    int last=0;
    while(stac.size() && w[stac.top()]>=w[num]){
        last=stac.top();
        stac.pop();
    }
    if(stac.empty()){
        root=num;
        ch[num][0]=last;
        if(last)fa[last]=num;
        stac.push(num);
        return;
    }
    ch[stac.top()][1]=num;
    fa[num]=stac.top();
    ch[num][0]=last;
    if(last)fa[last]=num;
    stac.push(num);
}
stack<int>dfs;
void work(int rt){
    bool vis[maxn]={0};
    vis[0]=1;
    dfs.push(rt);
    while(dfs.size()){
        int u=dfs.top();
        if(!vis[u]){
            vis[u]=1;
            printf("%d ",va[u]);
        }
        if(!vis[ch[u][0]])
            dfs.push(ch[u][0]);
        else if(!vis[ch[u][1]])
            dfs.push(ch[u][1]);
        else dfs.pop();
    }
}
int main(){
    File();
    scanf("%d",&n);
    REP(i,1,n)scanf("%d",&va[i]);
    REP(i,1,n)scanf("%d",&w[i]);
    REP(i,1,n)qu[i]=i;
    sort(qu+1,qu+n+1,cmp);
    REP(i,1,n)insert(qu[i]);
    work(root);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值