POJ 1785 Binary Search Heap Construction 【笛卡尔树构造,线段树RMQ(Range Max/Min Query)】

博客详细介绍了如何使用笛卡尔树解决POJ 1785问题,满足搜索序和堆的性质。内容包括标准解法——笛卡尔树的构建过程,以及分治和RMQ(Range Max/Min Query)方法,特别是线段树在解决区间最大值查询中的应用。同时提到了Treap与笛卡尔树的关系。

题目注意: 两个要求,1. 第一关键字满足搜索序,2. 第二关键字满足堆的性质。


1. 这道题目的标准(简单)解法:笛卡尔树

  首先将第一关键字(搜索序的关键字)排序(笛卡尔树中序遍历结果是按照第一关键字升序的),然后逐个向树中插入元素,这时只需要考虑后插入的元素的优先级即可。

a、优先级比上一个节点优先级高:将这个节点调整为根节点。

b、优先级比上一个节点优先级低:将这个节点设为当前节点的右儿子。


2. 但是还有: 分治+RMQ, 其中,RMQ问题有两种方法:线段树和Sparse Table算法。

依然是现按第一关键字排序,然后就是区间询问最大值,这个最大值就是整个树的根,然后分别对左右两个区间进行查询即可。


笛卡尔树定义:

笛卡尔树是一棵二叉树,树的每个节点有两个值,一个为key,一个为value。光看key的话,笛卡尔树是一棵二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大;光看value的话,笛卡尔树有点类似堆,根节点的value是最小(或者最大)的,每个节点的value都比它的子树要大。


补充:

Treap:treap和笛卡尔树的结构是相同的。



这道题目就是一道裸笛卡尔树的构造:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct node {
    int val, lson, rson, fa;
    char s[100];
} a[50005];
int n;
bool cmp(node x, node y) {
    return strcmp(x.s, y.s) < 0;
}
void Insert(int now) {
    int j = now - 1;
    while (a[j].val < a[now].val) j = a[j].fa;
    a[now].lson = a[j].rson;
    a[j].rson = now;
    a[now].fa = j;
}
void Traval(int now) {
    if (now == 0) return ;
    printf("(");
    Traval(a[now].lson);
    printf("%s/%d", a[now].s, a[now].val);
    Traval(a[now].rson);
    printf(")");
}
int main() {
    while (scanf("%d", &n) == 1 && n) {
        for (int i=1; i<=n; i++) {
            scanf(" %[a-z]/%d", a[i].s, &a[i].val);
            a[i].lson = a[i].rson = a[i].fa = 0;
        }
        sort(a+1, a+n+1, cmp);
        a[0].val = 0x3f3f3f3f;
        a[0].lson = a[0].rson = a[0].fa = 0;

        for (int i=1; i<=n; i++)
            Insert(i);
        Traval(a[0].rson);
        printf("\n");
    }
    return 0;
}




线段树RMQ版本:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct node {
    char s[100];
    int val;
} a[50005];
int n, v[50005<<2];

void build(int l, int r, int rt) {
    if (l == r) {
        v[rt] = l;
        return ;
    }
    int mid = (l + r) >> 1;
    build(l, mid, rt<<1);
    build(mid+1, r, rt<<1|1);
    v[rt] = (a[v[rt<<1]].val > a[v[rt<<1|1]].val) ? v[rt<<1] : v[rt<<1|1];
}
int query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R)
        return v[rt];
    int ret1, ret2, mid = (l + r) >> 1;

    ret1 = ret2 = 0;
    if (L <= mid) ret1 = query(L, R, l, mid, rt<<1);
    if (mid < R) ret2 = query(L, R, mid+1, r, rt<<1|1);
    if (ret1 == 0) return ret2;
    if (ret2 == 0) return ret1;
    return (a[ret1].val > a[ret2].val) ? ret1 : ret2;
}
void print(int l, int r) {
    if (l > r) return ;
    if (l == r) {
        printf("(%s/%d)", a[l].s, a[l].val);
        return ;
    }
    int m = query(l, r, 1, n, 1);
    printf("(");
    print(l, m-1);
    printf("%s/%d", a[m].s, a[m].val);
    print(m+1, r);
    printf(")");
}
bool cmp(node x, node y) {
    return strcmp(x.s, y.s) < 0;
}
int main() {
    while (scanf("%d", &n) == 1 && n) {
        for (int i=1; i<=n; i++)
            scanf(" %[a-z]/%d", a[i].s, &a[i].val);

        sort(a+1, a+n+1, cmp);
        build(1, n, 1);
        print(1, n);
        printf("\n");
    }
    return 0;
}


评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值