一天天的为了啥。|

lgh_2009

园龄:1年粉丝:1关注:0

笛卡尔树学习笔记

笛卡尔树是一棵二叉树,满足:

  1. 每个节点的编号满足二叉搜索树的性质。
  2. 节点 \(i\) 的权值为 \(p_i\),每个节点的权值满足小根堆的性质。

建树

考虑一颗笛卡尔树由二元组 \((k,v)\) 构成,k为数组下标,所以天然是升序排序,\(v\) 为数组元素。

笛卡尔树=堆+基于数组下标的二叉搜索树。

大根笛卡尔树就是满足大根堆性质的笛卡尔树,小根笛卡尔树满足小根堆性质。

以下拿最小堆为例:

  1. 单纯实现一个最小堆,我们使用栈作为辅助空间来构造堆.当一个数组元素要构造节点时,他可以成为栈顶元素的子节点(比栈顶元素大),或者成为栈顶元素的父节点(比栈顶元素小)

  2. 还要将最小堆变成二叉搜索树。一个已入栈元素的下标<后入栈元素的下标,所以在第一步中,变成子节点的元素必须要成为右节点;变成父节点的元素,栈中的元素必须成为他的左子树,这样才保持了基于下标的二叉搜索树性质。

本质是不断左旋新插入的节点直到堆的性质满足,所以二叉搜索树的性质也得以保存。

int k,rt;
inline void buildtree() {
k=top;
for(int i=1; i<=n; i++) {
while(k&&a[st[k]]>a[i]) --k;
if(k) rson[st[k]]=i;
if(k<top) lson[i]=st[k+1];//接在左子树
st[++k]=i;//用栈维护链
top=k;
}
rt=st[1];
}

应用

  1. 构造笛卡尔树利用性质解决问题

HDU1506

题目大意:\(n\) 个位置,每个位置上的高度是 \(h_i\),求最大子矩阵。

考虑以 \((i,h_i)\) 建出笛卡尔树,发现对于每个节点,有且仅有这个节点的子树内所有节点的 \(h\) 值必定不小于这个节点的值。所以考虑以每个点的值作为矩形长,这个点对答案的贡献即为 \(h_i\times siz_i\),其中 \(siz\) 表示子树大小。dfs 一遍即可。

const int maxn=100010;
ll res;
int n;
int a[maxn];
struct node {
int val,fa,x,ls,rs;
} t[maxn];
int dfs(int x) {
if(!x) return 0;
// cerr<<x<<' '<<t[x].ls<<' '<<t[x].rs<<'\n';
int siz=dfs(t[x].ls)+dfs(t[x].rs);
gmax(res,(ll)(siz+1)*t[x].val);
return siz+1;
}
void buildtree() {
for(int i=1; i<=n; i++) {
int k=i-1;
while(t[k].val>t[i].val) k=t[k].fa;
t[i].ls=t[k].rs;
t[k].rs=i;
t[i].fa=k;
t[t[k].ls].fa=i;
}
rt=t[0].rs;
}
int main() {
while(rd(n),n!=0) {
t[0].ls=t[0].rs=0;
for(int i=1; i<=n; i++) rd(a[i]),t[i].x=i,t[i].val=a[i],t[i].ls=t[i].rs=t[i].fa=0;
buildtree();
res=0;
dfs(rt);
printf("%lld\n",res);
}
return 0;
}
  1. 笛卡尔树上 dp

对于一类带有最大值计数的 dp,可以考虑笛卡尔树的节点对子树的贡献。

  1. 笛卡尔树上分治

本文作者:lgh_2009

本文链接:https://www.cnblogs.com/lgh-blog/p/18038655

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   lgh_2009  阅读(47)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起