笛卡尔树学习笔记
定义:权值满足堆性质,键值满足二叉搜索树性质的树。容易发现,Treap 就是一种笛卡尔树。
若未特别提及,下文的笛卡尔树权值均满足小根堆的性质。
构建方式
容易用 Treap 做到 \(O(n\log n)\) 建树。但下文会将一种使用单调栈做到 \(O(n)\) 建树的做法。
考虑用单调栈维护笛卡尔树上的一条右链。
每次加入一个点就不断弹栈知道找到它的父亲。那么它肯定是父亲的右儿子,而它的左儿子就是父亲原本的右儿子。
性质与应用
期望深度
- 随机一个排列,问笛卡尔树上某个点的期望深度。
若一个点 \(j\) 在笛卡尔树上是 \(i\) 的祖先(不妨设 \(j<i\)),那么说明 \(j\) 是区间 \([j,i]\) 中最小的一个。这种情况发生的概率为 \(\frac{1}{i-j+1}\),所以可以得到点 \(i\) 的期望深度为
其中 \(H(n)=\sum_{i=1}^n\frac{1}{i}\),其实就是调和级数。
所以在随机数据下,笛卡尔树的期望深度是 \(O(\log n)\) 的。(这也是 Treap 的期望深度)
同构概率
笛卡尔树同构当且仅当对于再每个点子树内的数都小于它,这样的概率为 \(\frac{1}{siz}\)。所以总概率为
最值分治
传统分治一般是找到区间中点后往两边分治,时间复杂度为 \(T(n)=T(\frac{n}2)+O(n)\)。但有些问题不太好用区间中点分治,这是就需要找到一些满足特殊性质的点分治。
例如与区间 \(\min\) 或 \(\max\) 有关的,就可以取区间的最值来分治。那么扩过这个最值点的区间的 \(\min\) 或 \(\max\) 就是已知的了。
但要如何保证时间复杂度呢?
假设当前分治到区间 \([l,r]\),且最值点为 \(p\)。如果每次能以 \(O(\min(p-l+1,r-p+1))\) 的时间复杂度处理跨过 \(p\) 的信息,那么就可以做到 \(O(n\log n)\)。
证明就相当于把启发式合并的构成倒过来。(所以最值分治又称启发式分裂)
例题
Luogu P5854 【模板】笛卡尔树
板子,不讲了。
[AGC028B] Removing Blocks
考虑按删除时间建笛卡尔树,那么一个点的贡献为 \(a_i\times dep(i)\)。
由上文可知,一个点的期望深度为 \(H(i)+H(n-i+1)-1\),再乘上 \(n!\) 即为所有方案的深度和。
其实如果直接枚举祖先 \(j\) 计数也不是不可以,只不过式子不是很好优化。但这里用期望就简洁很多了。
QOJ242 RMQ Similar Sequence
有一个引理是:再 \([0,1]\) 中随机两个实数,相等的概率为 \(0\)。
所以可以先假定随机的是一个排列,算出满足条件的排列的概率后再乘上期望权值和。
显然满足条件等价于笛卡尔树同构,这样的概率详见上文。
而期望权值和可以对于每个随机变量分别考虑。每个随机变量的期望是 \(\frac{1}{2}\),所以期望总和为 \(\frac{n}{2}\)。
Luogu P4755 Beautiful Pair
最值分治板子。
分治处理跨过 \(mid\) 的贡献,枚举一个 \(i\),那么 \(j\) 要满足 \(a_j\le\frac{a_{mid}}{a_i}\)。
区间数范围内的点的个数可以离线扫描线或主席树。
[ABC282Ex] Min + Sum
同上,按序列 \(A\) 的 \(\min\) 最值分治。那么满足条件的另一个端点具有单调性,可以直接二分。
CF1220F Gardener Alex
很巧妙的题。
考虑找到最小值所在位置并将其作为开头。
由于最小值一定是笛卡尔树的根,所以左右子树分别对应着此时序列的一段后缀和一段前缀。
先考虑前缀的情况,后缀是类似的。
考虑在单调栈构建笛卡尔树的同时维护出每个点的深度及其子树内距其最远点的距离,那么就可以距其算出加入每个点后笛卡尔树的最大深度了。
具体实现时需要注意细节。
[USACO19DEC] Tree Depth P
显然一个点的深度为其祖先个数(这里视自己为自己的祖先),于是可以枚举 \(u,v\) 表示 \(v\) 为 \(u\) 的祖先。然后计数满足以下条件的排列:
逆序对数量恰好为 \(k\) 且 \(\min_{i=\min(u,v)}^{\max(u,v)}a_i=a_v\)。
先考虑只计数逆序对数量为 \(k\) 的排列,当加入第 \(i\) 个位置时可能多产生的逆序对数目在 \([0,i-1]\) 中。写成 OGF 得到答案为
这可以用背包求出。
再考虑加上后面的限制,此时考虑以某种特定的顺序加入。即先加入 \([\min(u,v),\max(u,v)]\) 之间的,再加入前缀和后缀。容易发现,只有当加入点 \(v\) 时的贡献是已知的,为 \(\max(0,v-u)\),其余点的贡献都是和上述一样的。
设 \(F(x)=\prod_{i=0}^{n-1}\sum_{j=0}^{i-1}x^j\),考虑从中撤销掉 \(\max(0,v-u)\) 的贡献(即多项式除法)。若记 \(t=\max(0,v-u)\),那么答案为
可以先枚举 \(t\) 算出系数,做到 \(O(n^3)\)。
[ZJOI2012] 小蓝的好友
考虑枚举底边 \((x,l,r)\),表示在第 \(x\) 行的第 \([l,r]\) 列。若记 \(h_i\) 表示第 \(i\) 列资源点中最大的行,那么答案为
考虑建笛卡尔树,那么后面那部分贡献是好算的。
单点修改就直接用 Treap 维护。由于数据随机,所以复杂度是正确的。
[IOI 2018] meetings 会议
一开始就想歪了。
考虑 DP,设 \(f_{l,r}\) 表示区间 \(l,r\) 的答案。转移容易想到找到区间 \([l,r]\) 最大值的位置 \(p\),那么有
最大值的位置容易让人想到笛卡尔树。假设现在询问的区间为 \([L,R]\),最大值位置为 \(x\),那么只需求出 \(f_{L,x-1}\) 和 \(f_{x+1,R}\) 就可以推出 \(f_{L,R}\)。
再考虑 \(f_{L,x-1},f_{x+1,R}\) 等价于什么。若点 \(x\) 对应的区间为 \([l,r]\),那么其左右儿子的区间分别为 \([l,x-1],[x+1,r]\)。所以只需对于笛卡尔树上的区间 \([l,r]\),维护出 \(f_{l,i},f_{i,r}\) 即可(\(l\le i\le r\))。
考虑笛卡尔树上深度相同的点,所对应的区间是不交的。所以可以记 \(F_i=f_{l,i},G_i=f_{i,r}\),那么空间可以做到线性。
然后考虑如何从 \([l,x-1],[x+1,r]\) 推到 \([l,r]\)。下文以 \(F_i\) 为例。
对于 \(l\le i<x\) 的,\(F_i\) 是不会改变。而 \(F_x\) 显然等于 \(F_{x-1}+a_x\)。
再考虑 \(x<i\le r\) 的,可以先出递推式:
观察可以发现,左边的式子每次增加 \(a_x\),右边的式子每次至多增加 \(a_x\)。所以说取到左边的一定是一段前缀。
于是可以在线段树上二分出这一段前缀,那么操作就变成了区间赋值一次函数和区间加。懒标记维护即可。