笛卡尔树

闲的没事翻新题,突然想起笛卡尔树还没学,于是写了写笛卡尔树的模板题。

题意

  • 给一个排列p1pn,i号点权值为pi,要求建一棵以编号为关键字的二叉搜索树(中序序列为1n),且以权值为关键字的小根堆。

  • n <= 1e7

思路分析

难度在于O(n)建树。但既然编号是连续的,那么我们就每次使劲往右插即可。因此我们要维护一条从根节点一直向右的链

又因为要维护小根堆性质,每次在链上找到一对相邻点,使得父亲权值大于它,且儿子权值小于它。把儿子设为自己的左儿子,并把自己设为父亲的右儿子即可。

Code:

for (register int i = 1; i <= n; ++i) {
	while (top && val[sta[top]] > val[i])	ls[i] = sta[top--];
	if (top)	rs[sta[top]] = i;
	sta[++top] = i;
}

然而并不知道它有什么用。

一个不那么显然的性质

其实还是挺显然的

笛卡尔树上每个节点的子树中的编号集合一定是一段连续的区间,不过一段连续的区间却不一定是一个连通块。

实用版建树:

维护每个点的子树编号区间(这个点一定是区间中的权值的最值点),找出区间中该点左右的最值点,该点向那两个点连边,然后递归子问题。

例题

SP3734 PERIODNI - Periodni

(大概是最经典的笛卡尔树题了)

给个直方图,问放 \(k\) 个不在同一行同一列的点的方案数。

我们发现直方图其实还可以横着划分,划分成一个个方块(见 lhm_大佬的题解),然后会出现树形结构(类似树形依赖关系),用类似树形背包的组合计数DP解决即可。

题解中的图片

这个树形结构其实就是笛卡尔树。

可见,笛卡尔树擅长把序列最值“瓶颈”问题转化为树上的问题。

P3246 [HNOI2016]序列

这题似乎是想用笛卡尔树优化rmq的一个log,但似乎那个题解被 hack 了。

题解在这

强制在线1e7询问的加强版(在鸽)

P5044 [IOI2018] meetings 会议

看不懂题解+看不懂题解代码+不会线段树,可能还要鸽一段时间。

lsr大佬的题解

不知道为啥我刚要做,lsr大佬就写好题解了,然后我还看不懂qaq

2020.8.28 Update:

写完了。感谢 ywy 学长的讲解!

笛卡尔树上 DP,用线段树来优化。不过用到笛卡尔树的一个套路:按照最大值把询问区间拆成两半,这样的话每一半都会有一侧是完整的,利用这个性质方便解题。

jzp 的题解

posted @ 2020-08-28 13:09  JiaZP  阅读(160)  评论(0编辑  收藏  举报