笛卡尔树

创建时间:2025-12-30


笛卡尔树的定义

笛卡尔树是一种特殊的二叉树,它同时具备二叉搜索树和二叉堆的性质。

具体的,笛卡尔树的每个节点都有一个键值 \(key\) 和权值 \(val\),而一个节点 \(u\) 的左子树中所有节点的 \(key\) 都小于等于 \(key_u\),右子树中所有结点的 \(key\) 都大于等于 \(key_u\),且 \(u\) 一定是 \(u\) 的子树中 \(val\) 最大的节点
之一。

在实际问题中,键值通常等于序列的索引,当然也有题目中不等于。

构造笛卡尔树

P5854 【模板】笛卡尔树 为例:给定序列 \(a_1, a_2, a_3, \cdots, a_n\),以 \(i\) 为键值,\(a_i\) 为权值建笛卡尔树。设 \(i\) 的左儿子、右儿子分别为 \(l_i, r_i\),输出 \(\operatorname{xor}_{i=1} l_i\)\(\operatorname{xor}_{i=1}^n r_i\)

为方便插入节点,我们称在一棵笛卡尔树上从根开始一直向右子树走直到右子树为空,得到的链为该笛卡尔树的右链。

例如下图中笛卡尔树的右链为红色部分(节点编号为其键值):

现在考虑在右链末端插入一个键值为 \(10\) 的节点 \(x\),显然该操作不影响笛卡尔树的二叉搜索树性质。但这么做可能破坏笛卡尔树的二叉堆性质,故应该在 \(x\) 的权值比 \(x\) 父节点的更优时将 \(x\) 上浮,直到 \(x\) 成为根节点或 \(x\) 的权值比 \(x\) 的父节点劣。像这样:

直接上浮保证了笛卡尔树的二叉堆性质。然而,这可能又破坏了笛卡尔树的二叉搜索树性质,这时只需要把节点 \(7\) 挂在 \(x\) 的左子树上就行了,即让 \(7\) 作为 \(x\) 的左儿子。

这样就成功将 \(x\) 插入笛卡尔树了。不难发现,这既没有破坏二叉搜索性质,有没有破坏二叉堆性质。

那么怎么实现这一过程呢?

根据笛卡尔树的定义知右链是一条键值单调,权值也单调的链,所以想到用单调栈维护笛卡尔树的右链。因为新节点 \(x\) 上浮过程中被顶替的所有祖先节点在 \(x\) 插入完成后都在 \(x\) 的左子树中,所以它们都不再是右链的一部分。即每个节点只会进栈一次,最多出栈一次,总建树时间复杂度为 \(O(n)\)

代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 1e7 + 50;

int n, p[N], top, stk[N];

struct Node {
	int lc, rc, val;
} tr[N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0); 
	
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> p[i];
	
	for (int i = 1; i <= n; i++) {
		tr[i].val = p[i];
		int k = top;
		while (k && p[i] < p[stk[k]])
			k--;
		if (k)
			tr[stk[k]].rc = i;
		if (k < top)
			tr[i].lc = stk[k + 1];
		stk[top = ++k] = i;
	}
	
	long long ans1 = 0, ans2 = 0;
	for (int i = 1; i <= n; i++) {
		ans1 ^= 1ll * i * (tr[i].lc + 1);
		ans2 ^= 1ll * i * (tr[i].rc + 1);
	}
	cout << ans1 << ' ' << ans2 << '\n';
	return 0;
} 

笛卡尔树的应用

笛卡尔树的重要性质:

  1. 笛卡尔树建树的时间复杂度为 \(O(n)\),空间复杂度也为 \(O(n)\),在卡空间或者查询不是特别多的情况下可以代替 ST 表
  2. 笛卡尔树的每个节点的管辖范围是该节点作为区间最大/小值的最大区间
  3. 区间 \([l,r]\) 的最值为从下往上第一个管辖范围包含 \([l,r]\) 的节点的权值,且这个节点是 \(\operatorname{LCA}(l,r)\)

其中第 2 条很容易和 DP 一起考,通过笛卡尔树上 DP 优化和区间最值有关的状态转移。

运用笛卡尔树本身的定义以及这些性质,可以解决很多题目,例:

P1377 [TJOI2011] 树的序(笛卡尔树的建树)
P4147 玉蟾宫(笛卡尔树的建树及基础性质)
P3793 由乃救爷爷(笛卡尔树维护静态区间最大值)
CF1580D Subsequence(笛卡尔树上DP)
P5044 [IOI 2018] meetings 会议(笛卡尔树上分治+线段树优化DP)

posted @ 2026-06-02 16:46  xubaichuan  阅读(10)  评论(0)    收藏  举报