定义
对于一个简单的序列:
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=1~n 的顺序依次加入节点并调整树的结构,使得当前的树为子序列
[
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,i−1],[i+1,r] 两部分,
[
l
,
i
−
1
]
[l,i-1]
[l,i−1] 的最小值就是
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 1~n
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 0~n−1
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;
}
}