笛卡尔树(Cartesian Tree)
笛卡尔树(Cartesian Tree)
1. 定义
根据序列构造的满足以下性质的树:
- 二叉搜索树性质(BST):\(key_{ls} \le key_x \le key_{rs}\),\(key\) 默认为下标。
- 堆性质:\(val_{x} \le val_{ls} \le val_{rs}\).
2. 构造
- 如果有 \(key\) 作为第一关键字,则按 \(key\) 升序排序,否则默认下标为 \(key\);
- 使用单调栈维护右链,按照 \(key\) 升序的顺序添加节点。
- 加入一个新节点 \(i\) 时,找到右链上第一个 \(val_j \le val_i\) 的节点,将 \(rs_j\) 作为 \(i\) 的左儿子,\(i\) 作为新的 \(rs_j\).
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 5;
int n, p[N], ls[N], rs[N], fa[N], root;
long long ans1, ans2;
inline void read(int &x)
{
char c = '.'; x = 0;
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
stack<int> stk;
int main()
{
read(n);
for(int i = 1; i <= n; i++) read(p[i]);
stk.push(0);
for(int i = 1; i <= n; i++)
{
while(!stk.empty() && p[stk.top()] >= p[i])
fa[stk.top()] = i, ls[i] = stk.top(), stk.pop();
if(stk.top()) fa[i] = stk.top(), rs[stk.top()] = i;
stk.push(i);
}
for(int i = 1; i <= n; i++)
ans1 ^= 1ll * i * (ls[i] + 1), ans2 ^= 1ll * i * (rs[i] + 1);
cout << ans1 << ' ' << ans2;
return 0;
}
3. 应用
3.1. 求区间最小值(RMQ)
建立小根笛卡尔树,区间 \([a, b]\) 的最小值是 \(val_{\operatorname{LCA}(a, b)}\).
利用 BST 的性质可以快速求 LCA.
复杂度为树的深度,随机数据下为 \(O(n \log n)\).
int find_min(int a, int b)
{
int lca = root;
while(lca < a || lca > b)
{
if(lca < a) lca = rs[lca];
if(lca > b) lca = ls[lca];
}
return v[lca];
}
3.2. 求最小值范围
给定一个无序序列和一个下标,求对应的值是多大范围内的最小值。
- 建立小根笛卡尔树,下标 \(i\) 对应的区间为 \(i\) 的子树中最左边的点到最右边的点。
3.3. 最大矩形
求下图的最大矩形(\(n \le 10^5\)):

- 建立小根笛卡尔树,每个点的贡献是 \(\text{子树大小} \times \text{深度}\).
4. 例题
4.1. P1350 车的放置
\(f_{i,j}\):在 \(i\) 的子树中放 \(j\) 个车的方案数。
\(f_{u, i} = \sum_{j=0}^i son_i \times j!\begin{pmatrix}i-1 \\j\end{pmatrix}\begin{pmatrix}w-(i-j) \\j\end{pmatrix}\).
\(son_i = \sum_{j=0}^i (f_{lc, j} \times f_{rc, i-j})\).
在 \(n \times m\) 的矩形中放 \(k\) 个车:\(C_n^k \times C_m^k \times k!\).
望穿寂夜晨曦至,雄鹰展翅图九天。

浙公网安备 33010602011771号