这是本文档旧的修订版!
一种二叉树,主要用于解决有关序列 $\text{RMQ}$ 问题和直方图问题,建树时间复杂度 $O(n)$。
笛卡尔树具有如下三条性质:
事实上,笛卡尔树可以对任意二元组进行建树,对第一关键字满足二叉搜索树性质,对第二关键字满足堆性质。
从上面性质也可以看出,笛卡尔树实际上就是一棵特殊的 $\text{Treap}$。
接下来考虑建树,事实上可以按顺序插入序列,每次插入时由于插入结点下标最大,所以只能不停跳右子树。
找到合适结点,取代该结点位置,并将该结点所在子树变成插入结点的左子树。
发现只需要用一个单调栈维护一下从根结点起沿途所有右儿子构成的树链即可,时间复杂度 $O(n)$。
const int MAXN=1e7+5,Inf=2e9; int Stack[MAXN],v[MAXN],ch[MAXN][2],root; void build(int n){//小根堆笛卡尔树 int top=0; v[0]=-Inf;Stack[++top]=0; _rep(i,1,n){ while(v[i]<v[Stack[top]])top--; ch[i][0]=ch[Stack[top]][1]; ch[Stack[top]][1]=i; Stack[++top]=i; } root=Stack[2]; }
给定一个 $N$ 列的表格,每列的高度为 $a_i$ 且底部对齐。
要求向表格中填入 $k$ 个相同的数,填写时要求不能有两个数在同一列,或在同一行且中间表格不间断。
问在取模意义下有多少种填法。
建立根据表格高度建立笛卡尔树,将表格划分分 $N$ 个矩形。
设 $f(i,j)$ 表示在以结点 $i$ 为根结点的子树所代表的区域中填写 $j$ 个数的填法种数,$H$ 为结点代表矩阵高度,$\text{sz}$ 代表结点子树大小。
$g(i,j)$ 表示在以结点 $i$ 为根结点的子树所代表的区域中填写 $j$ 个数且不在根结点代表的矩形填写数字的填法种数。
先在考虑从某个结点所代表的矩阵中选取 $k$ 个结点的选法。
易知从 $n\times m$ 的矩阵中选取 $k$ 个结点的总数为 $k!C_n^kC_m^k$,预处理 $k!$ 和 $k!$ 的逆即可快速求出。
容易得出状态转移方程
\begin{equation}H=H_u-H_{fa},\text{sz}_u=1+\text{sz}_{lson}+\text{sz}_{rson}\end{equation}
\begin{equation}g(u,i)=\sum_{j=0}^i f(\text{lson},j)\times f(\text{rson},i-j)\end{equation}
\begin{equation}f(u,i)=\sum_{j=0}^i g(u,j)\times (i-j)!C_H^{i-j}C_{\text{sz}_u-j}^{i-j}\end{equation}
边界条件为 $f(0,0)=g(0,0)=1$,时间复杂度 $O(nk^2)$。
另外,还可以用笛卡尔树 $+$ 树上背包解决上述问题,常数较小,其中 $\text{dp}$ 数组第一次转移表示 $g$,第二次转移表示 $f$。