【学习笔记】笛卡尔树
定义
一棵树,每个点有两个权值,如果 \(a\) 权值满足 \(BST\),\(b\)权值满足 \(heap\),那么这棵树 就是一棵笛卡尔树
一般是对于一个序列建笛卡尔树
其实 \(Treap\) 就是一个笛卡尔树,不过笛卡尔树不平衡,同时一棵子树的中序遍历对应原序列中的一个区间
构造
\(O(n)\) 建树
考虑用单调栈维护最右边的一条链,和单调栈求每个点作为哪些区间的 \(\rm{RMQ}\) 是类似的
代码:
for(int i=1;i<=n;++i){
while(top&&a[st[top]]>a[i]) ls[i]=st[top--];
if(top) rs[st[top]]=i;
st[++top]=i;
}
例题
SPOJ3734
我们看到这个是一个直方图,而且形状不规则,就考虑到建一棵笛卡尔树
然后把题目转成在矩形中选点(笛卡尔树把直方图变成一个一个的矩形,然后就是放点了)
这里有个结论:\(n \times m\) 的矩形中我们放 \(k\) 点,方案数为:\(C^{k}_n \times C^k_m \times k \space !\)
每个点都占一行和一列,所以两个组合数的乘积就是一个组合,排列数直接乘阶乘就好
然后是笛卡尔树上的 \(dp\), 这里转移就考虑枚举在每一岔上放几个点就行了,最后加一加,乘一乘
ZJOI2012 小蓝的朋友
枚举每行作为矩形的最下面一行,设 \(w_i\) 表示每列离当前行最近点的行号
这样子每行的答案可以表示为 \(\sum\limits_{i=1}^m\sum\limits_{j=i}^m \max\limits_{k=i}^j w_k\)
根据笛卡尔树的重要性质一个子树的中序遍历是原序列里面的连续区间,那么每个点的贡献可以用左右儿子求
使用 \(\texttt{fhq_treap}\)可以进行笛卡尔树的分裂和合并,支持修改一个点的值,同时仍然维护笛卡尔树形态,本题直接套用即可