📜  笛卡尔树(1)

📅  最后修改于: 2023-12-03 14:56:38.540000             🧑  作者: Mango

笛卡尔树介绍

笛卡尔树 (Cartesian Tree) 又称二叉树排序树,是一种特殊的二叉树,它满足以下条件:

  • 是一棵二叉树
  • 树的根结点对应于序列中的某个元素
  • 对于每个结点 v,它的左子树都对应于序列中排在 v 之前且值比 v 小的元素,而它的右子树则对应于序列中排在 v 之后且值比 v 大的元素

笛卡尔树的应用非常广泛,其中包括最大化区间问题(Max-Interval-Query)等。在此介绍笛卡尔树的构建方法,以及一个应用案例。

笛卡尔树构建方法

笛卡尔树最常见的构建方法是使用单调栈,具体步骤如下:

  1. 定义一个栈,用于存储序列中的元素
  2. 从左到右遍历序列中的每个元素
  3. 如果当前元素比栈顶元素大,那么将该元素作为栈顶元素的右孩子,并将该元素入栈。
  4. 如果当前元素比栈顶元素小,那么从栈中弹出元素,直到当前元素不小于栈顶元素,然后将该弹出的元素作为当前元素的左孩子,并将当前元素入栈。
  5. 遍历完整个序列后,将栈中的元素依次弹出,将它们作为右孩子放在它们的父节点下。
笛卡尔树应用案例

笛卡尔树最常见的应用是最大化区间问题(Max-Interval-Query),具体问题可定义为:给定一个长度为 n 的序列 A,和一个值 k,要求找到 A 中长度最长的连续子序列,使得子序列中最大的数不超过 k。这个问题可以使用笛卡尔树来解决。

实现步骤:
  1. 将序列 A 构造成笛卡尔树。
  2. 对于每个结点 v,计算以 v 为根的子树中最大的数 maxv。
  3. 对于每个结点 v,从它开始往下遍历,找到最深的那个子结点 u,使得在子树 u 中的最大值不超过 k。
  4. 对于每个结点 v,判断其子树是否符合要求,若符合则计算其深度,更新答案。
时间复杂度:
  1. 构造笛卡尔树的时间复杂度为 O(n)。
  2. 计算以 v 为根的子树中最大的数 maxv 需要遍历以 v 为根的子树,时间复杂度为 O(size(v))。
  3. 从结点 v 开始往下遍历,找到最深的那个子结点 u 的时间复杂度最多为 O(logn)。
  4. 总时间复杂度为 O(nlogn)。
示例代码
struct node {
    int val, maxv;
    node *l, *r;
    void pushup() {
        maxv = val;
        if (l) maxv = max(maxv, l->maxv);
        if (r) maxv = max(maxv, r->maxv);
    }
}*root, pool[MAXN], *cur = pool;

stack<node*> stk;

inline node* newNode(int val) {
    node* now = cur++;
    now->val = now->maxv = val;
    return now;
}

inline void Insert(int val) {
    node* now = newNode(val);
    if (stk.empty()) {
        root = now;
    } else {
        if (val > stk.top()->val) {
            stk.top()->r = now;
        } else {
            node* p;
            while (!stk.empty() && stk.top()->val > val) {
                p = stk.top(); stk.pop();
            };
            if (stk.empty()) {
                now->l = p; root = now;
            } else {
                stk.top()->r = now; now->l = p;
            }
        }
    }
    stk.push(now);
}

int dfs(node* u, int k) {
    if (!u) return 0;
    if (u->maxv > k) return 0;
    if (u->val > k) return dfs(u->l, k);
    return dfs(u->r, k) + max(dfs(u->l, k), u->r ? u->r->rmaxv : 0) + 1;
}

int query(int k) {
    return dfs(root, k);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int n, k;
    cin >> n >> k;
    for (int i = 1, x; i <= n; ++i) {
        cin >> x;
        Insert(x);
    }
    cout << query(k) << endl;
    return 0;
}

返回的代码片段中,我主要展示了笛卡尔树的遍历方法,及基于笛卡尔树的应用——最大化区间问题的实现代码。同时,对代码中的重要变量及函数做出了简单的注释,在记录方法的讲解时也做了相应体现。