笛卡尔树

定义

对于一个简单的序列: 9 , 3 , 7 , 1 , 8 , 12 , 10 , 20 , 15 , 18 , 5 9, 3, 7, 1, 8, 12, 10, 20, 15, 18, 5 9,3,7,1,8,12,10,20,15,18,5
p o s pos pos 表示原序列中的位置, v a l val val 表示该位置的值
笛卡尔树具有如下两个性质:
① : p o s ①:pos pos 满足二叉搜索树,左子树的 p o s pos pos 小于根,右子树的 p o s pos pos 大于根
② : v a l ②:val val 满足堆,左右子树的 v a l val val 大于根
在这里插入图片描述


建树

i = 1 ~ n i=1~ n i=1n 的顺序依次加入节点并调整树的结构,使得当前的树为子序列 [ 1 , i ] [1,i] [1,i] 所构成的笛卡尔树
若当前插入节点 i i i,因为 i i i 在区间 [ 1 , i ] [1,i] [1,i] p o s pos pos 最大, p o s pos pos满足二叉搜索树,因此 i i i 在根节点一直向右儿子,走到空节点
然后考虑 i i i v a l val val 值,由于 v a l val val 满足堆,因此只需要将 i i i 拼命往上移动,找到一个祖先 j j j,满足 j j j 的子树的 v a l val val 全大于 i i i v a l val val

然后 i i i 就成为了 j j j 的父亲, j j j 成了 i i i左子树
并且 i i i 要继承 j j j 的父亲, i i i 成为它原来父亲的 右儿子

这个过程可以用单调栈解决

详细过程可参考:笛卡尔树
时间复杂度: O ( n ) O(n) O(n)


一些性质:

① : ①: 下标 i i i 到下标 j j j 之间 ( i < j ) (i<j) (i<j) 的最小值,只需寻找 i i i j j j l c a lca lca
② : ②: i i i 为最小值的区间长度 = i = i =i 的子树大小,左端点为子树中 p o s pos pos 最小值,右端点为 p o s pos pos 最大值
③ : ③: 分治处理到 [ l , r ] [l,r] [l,r] 时,若最小值为 a i a_i ai i i i 即为此区间对应子树的根节点,相当于在笛卡尔树上递归分治
     然后将区间分为 [ l , i − 1 ] , [ i + 1 , r ] [l,i-1], [i+1,r] [l,i1],[i+1,r] 两部分, [ l , i − 1 ] [l,i-1] [l,i1] 的最小值就是 a l s [ i ] a_{ls[i]} als[i] [ i + 1 , r ] [i+1,r] [i+1,r] 的最小值为 a r s [ i ] a_{rs[i]} ars[i]


模板

① : ①: 下标为 1 ~ n 1 ~ n 1n

int st[maxn], fa[maxn];
int ls[maxn], rs[maxn];
 
void build() {
  int tail = 0;
  for(int i=1; i<=n; i++) {
      while(tail && a[st[tail]] > a[i]) ls[i] = st[tail], tail--;
      fa[i] = st[tail], fa[ls[i]] = i;
      if(fa[i]) rs[fa[i]] = i;
      st[++tail] = i;
  }
}

② : ②: 下标为 0 ~ n − 1 0 ~ n-1 0n1

int st[maxn], fa[maxn];
int ls[maxn], rs[maxn];
 
void build() {
    int tail = 0;
    for(int i=0; i<n; i++) {
        int t = tail;
        while(t && a[st[t]] > a[i]) t--;
        if(t < tail) ls[i] = st[t+1], fa[ls[i]] = i;
        if(t) rs[st[t]] = i, fa[i] = st[t];
        st[++t] = i; tail = t;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值