笛卡尔树:在范围最值查询、范围top k查询等问题上有广泛应用。它具有堆的有序性,中序遍历可以输出原数列。


一、定义与性质

首先先明确,笛卡尔树的构建是根据两个键值来的,第一个是他的if顺序,另一个是他维护的权值(最大或者最小),满足每个节点的根都是两边区间的最小值(性质),比如说 序列 :3 1 5 4 2 它的笛卡尔树就是这样的:

首先,可以看出它满足 id从左到右的顺序,其次每个根都是两边区间最小值。


二、建树

笛卡尔树的建立与单调栈有关,因为这个树要满足id从左到右建立,所以新来的元素 只有两种情况 1.成为 父亲节点(在右) 2.成为儿子节点(在右),但建立过程中会出现 3 1 5 4 2 的情况 5 比1 小 所以 5 成为1 的右儿子,但4又来了,4比5小所以4应该成为5的右父亲,就这么一个过程。具体操作用单调栈还实现:

void get_tree()
{
    int s=0;
    for(int i=1;i<=n;i++)
    {
        while(s&&num[i]<=num[st[s]])
            l[i]=st[s--];//难
        if(st[s]) r[st[s]]=i;//难
        st[++s]=i;
    }
}

用个最简单的例子 来解释 最难理解的那一部分:2 3 4 5 1 和 1  3  4  5 2

2 3 4 5 1:在1到来之前 的链是这样的 3是2的右儿子,4是3的右儿子,5是4的右儿子:

1来了之后 1尝试着去***去(因为1比之前的小 ,1插入的位置可以确定 哪个区间的最小值)

1在尝试他能插到哪个地方结果插到了顶部,那么这个树的定点就会成为1的左二子,连过去就可以了。

同样地:1 3 4 5 2 刚开始  1 3 4 5 是一条链

最后到1 停了,说明2是1的右儿子,所以1的右儿子指向了2,因为3 4 5 在2左边,所以2的左二子是3,所以该图构建结束。

其实过程就是单调栈的过程所以用单调栈维护建树就可以了。


总结:

笛卡尔树在处理区间的topk问题上还是挺好用的,而且每一个最小值维护的区间可以轻松的看出。方便解决问题