【全】笛卡尔树学习笔记
背景
发现自己不会笛卡尔树,于是前去 oi-wiki 学习并完成了部分例题。
这篇文章将会详细讲解笛卡尔树,基本是作者自己经过学习提炼出的核心内容。
笛卡尔树(正文)
定义
不讨论空树,笛卡儿树是一棵二叉树,其中每个节点有一个二元组信息
重要性质
我们这里讨论对任意正整数序列(元素值互不相同)构建小根笛树。
当一棵笛卡尔树的所有
一棵子树对应原序列的一个区间。
构建
我们这里仍然讨论对任意正整数序列(元素值互不相同)构建小根笛树。形式化地,
借 oi-wiki 图一用。
那么我们考虑从左往右依次将
红色部分表示笛卡尔树的右链。通过这个过程,我们能总结出插入(假设树非空)的策略。我们通过某种手段维护出这个右链,由于
那么如何维护右链呢?显然我们使用一个栈来维护即可。(某些地方称为单调栈,但其实就是普通的栈)由于每个元素最多进出一次栈,整个建树的时间复杂度是 int 范围内给出代码(改编自 oi-wiki)。
// stk 维护笛卡尔树中节点对应到序列中的下标
for (int i = 1; i <= n; i++) {
int tp = top; // top 表示操作前的栈顶,tp 表示当前栈顶
while (tp && w[stk[tp]] > w[i]) k--; // 维护右链上的节点
if (tp) rs[stk[tp]] = i; // i 父亲存在
if (tp < top) ls[i] = stk[tp + 1]; // 当前元素.左儿子 := 上一个被弹出的元素
stk[top = ++tp] = i; // 将 i 插入右链
}
应用
这里以例题形式讲解笛卡尔树的应用。
Hdu1506 最大子矩形
题目翻译:若干组数据,每次给出
题解(也是我的)。
P1377 树的序
如果我们能建出树那么跑一遍先序遍历即可。但是如果模拟题目的建树方式显然时间复杂度
观察性质可以发现这棵树以权值对
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, k[N], root, ls[N], rs[N], top, stk[N];
void dfs(int u) {
cout << k[u] << ' ';
if (ls[u]) dfs(ls[u]);
if (rs[u]) dfs(rs[u]);
return void();
}
vector <int> vec;
inline bool cmp(const int x, const int y) { return k[x] < k[y]; }
int main() {
cin.tie(0) -> sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> k[i], vec.emplace_back(i);
sort(vec.begin(), vec.end(), cmp);
for (const int &x : vec) {
int tp = top;
while (tp && stk[tp] > x) --tp;
if (tp) rs[stk[tp]] = x;
else root = x;
if (top && tp < top) ls[x] = stk[tp + 1];
stk[++tp] = x, top = tp;
}
dfs(root);
return 0;
}
后记
好的题单:笛卡尔树题单。
感谢阅读!