单调栈求笛卡尔树
笛卡尔树实际上是对一个整数序列建树,以这个整数序列为优先级,下标为数,建Treap
当优先级互不相同,笛卡尔树是惟一的
笛卡尔树可以通过区间rmq并维护一个位置在 O(n log n) 的时间内求出来,不过我们有 O(n) 的做法
我们用一个单调栈维护当前优先级递减的序列,然后不断弹栈找到合适位置,把最后一个弹走的元素的父亲标记为她,再把她的父亲标记为栈顶就行
【题目描述】
Treap 是一种平衡二叉搜索树,除二叉搜索树的基本性质外,Treap 还满足一个性质:
每个节点都有一个确定的优先级,且每个节点的优先级都比它的两个儿子小(即它的优先级满足堆性质)。
不难证明在节点的优先级都事先给定且互不相同时,对应的 Treap 有且仅有一个。
现在,给定 n 个数和每个数对应的优先级,求出对应的以数的大小作为二叉搜索树比较依据的 Treap 的先序遍历结果。
对先序遍历的定义是:先访问根节点,再访问左子树,最后访问右子树。
【输入格式】
第一行一个数 n 表示数的个数。
第二行 n 个数表示每个数的大小。
第三行 n 个数表示每个数对应的优先级。
【输出各式】
一行 n 个数,表示 Treap 的先序遍历结果(对于每个节点,输出对应的数)。
#include <cstdio>
#include <algorithm>
using namespace std;
struct data { int val, pri; } a[100010];
int n, fa[100010], s[100010], lc[100010], rc[100010], top;
void dfs(int x)
{
printf("%d ", a[x].val);
if (lc[x]) dfs(lc[x]);
if (rc[x]) dfs(rc[x]);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i].val);
for (int i = 1; i <= n; i++) scanf("%d", &a[i].pri), a[i].pri = -a[i].pri;
sort(a + 1, a + 1 + n, [](const data &a, const data &b) { return a.val < b.val; });
s[top = 1] = 1;
for (int i = 2; i <= n; i++)
{
if (a[i].pri > a[s[top]].pri)
{
while (top > 0 && a[i].pri > a[s[top]].pri) top--;
fa[s[top + 1]] = i;
}
fa[i] = s[top], s[++top] = i;
}
for (int i = 1; i <= n; i++) if (fa[i])
{
if (fa[i] > i) lc[fa[i]] = i;
else if (fa[i] < i) rc[fa[i]] = i;
}
for (int i = 1; i <= n; i++) if (fa[i] == 0) dfs(i);
return 0;
}