笛卡尔树
笛卡尔树
笛卡尔树结构由Vuillmin在解决范围搜索的几何数据结构问题时提出。从数列中构造一棵笛卡尔树可以线性时间完成,需要采用单调栈找到在该数列中的所有最近小数。
笛卡尔树是一种二叉树,每一个结点由一个键值二元组 \((w,v)\) 构成。
\(w\)满足堆的性质,\(k\)满足二叉搜索树的性质。
用于解决\(RMQ\)问题。
笛卡尔树的性质
无相同元素的数列构造出的笛卡尔树具有下列性质
- 结点一一对应于数列元素。即数列中的每个元素都对应于树中某个唯一结点,树结点也对应于数列中的某个唯一元素
- 中序遍历(in-order traverse)笛卡尔树即可得到原数列。即任意树结点的左子树结点所对应的数列元素下标比该结点所对应元素的下标小,右子树结点所对应数列元素下标比该结点所对应元素下标大。
- 树结构存在堆序性质,即任意树结点所对应数值大/小于其左、右子树内任意结点对应数值
笛卡尔树根结点为数列中的最大/小值,树本身也可以通过这一性质递归地定义:根结点为序列的最大/小值,左、右子树则对应于左右两个子序列,其结点同样为两个子序列的最大/小值。
因此,上述三条性质唯一地定义了笛卡尔树。
若数列中存在重复值,则可用其它排序原则为数列中相同元素排定序列,如将数组下标视为键值\(w\)。
算法步骤
- 定义\(h\)值数列,\(ls\)笛卡尔树左儿子,\(rs\)笛卡尔树右儿子,\(stk\)单调栈,\(top\)栈顶,\(k\)当前栈顶
- 将元素安装键值\(w\)排序,一个一个插入当前笛卡尔树,根据笛卡尔树的性质,我们插入的节点必然在右链上。
- 对于节点\(u_{new}\)从栈顶\(top\)向下比较,当发现当前右链有节点\(u_x\)其键值\(v_x < v_{u_{new}}\),就把\(u_{new}\)接到\(u_x\)的右儿子,而原本\(u_x\)的右儿子就变成了\(u_{new}\)的左儿子。
复杂度分析
每个数最多进出右链一次。用栈中维护当前笛卡尔树的右链上的结点。一个点不在右链上了就把它弹掉。这样每个点最多进出一次复杂度\(O(n)\)
模板题
P5854
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
int rs[N],ls[N],stk[N],h[N],n,top;
inline int read(){
int res = 0 ,flag = 1;
char ch = getchar();
while(ch < '0'||ch > '9'){
if(ch == '-')flag = -1;
ch = getchar();
}
while(ch >= '0'&&ch <= '9'){
res = (res<<1)+(res<<3) + (ch^48);
ch = getchar();
}
return res * flag;
}
int main()
{
cin >> n ;
for(int i = 1 ; i <= n ; i ++ )
{
h[i] = read();
int k = top;
while(k > 0 && h[stk[k]] > h[i])k--;
if(k)rs[stk[k]] = i;
if(k < top)ls[i] = stk[k + 1];
stk[++k] = i;
top = k;
}
ll ar = 0,al = 0;
for(int i = 1 ; i <= n ; i ++ )ar ^= 1ll * i *(ls[i] + 1),al ^= 1ll * i * (rs[i] + 1);
printf("%lld %lld",ar,al);
return 0;
}
练习题
P1377
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
int rs[N],ls[N],stk[N],h[N],n,top;
inline int read(){
int res = 0 ,flag = 1;
char ch = getchar();
while(ch < '0'||ch > '9'){
if(ch == '-')flag = -1;
ch = getchar();
}
while(ch >= '0'&&ch <= '9'){
res = (res<<1)+(res<<3) + (ch^48);
ch = getchar();
}
return res * flag;
}
void dfs(int x)
{
if(x)printf("%d ",x),dfs(ls[x]),dfs(rs[x]);
}
int main()
{
cin >> n ;
for(int x,i = 1 ; i <= n ; i ++ ){
x = read();
h[x] = i;
}
for(int i = 1 ; i <= n ; i ++ )
{
int k = top;
while(k > 0 && h[stk[k]] > h[i])k--;
if(k)rs[stk[k]] = i;
if(k < top)ls[i] = stk[k + 1];
stk[++k] = i;
top = k;
}
dfs(stk[1]);
return 0;
}
“风雪越是呼啸,雪莲越是绽放”