笛卡尔树学习笔记

Part 0. 闲话

nz 好闪,拜谢 nz


Part 1. 什么是笛卡尔树

定义:笛卡尔树是一种二叉树,每个节点有两个值 ( x i , y i ) (x_i,y_i) (xi,yi)

特点:

  1. 如果只考虑 x i x_i xi ,那么笛卡尔树是一棵 二叉搜索树

  2. 如果只考虑 y i y_i yi ,那么笛卡尔树是一个 小根堆/大根堆 。

:在后面的题目中,每个节点表示为 ( i , a i ) (i,a_i) (i,ai) , 即编号和对应的值。


Part 2. 如何建一棵笛卡尔树

我们可以根据定义和性质来建树。

具体地,先来考虑 x i x_i xi ,为了满足二叉搜索树的性质,我们会把新插入的节点放得尽可能靠右。所以我们实际上就是要维护一个从左上到右下的链。因此,在保证 x i x_i xi 递增的情况下,构造的时间复杂度是线性的。

以小根堆为例,我们设当前要插入的点为 u u u,从后往前找到第一个小于等于 u y u_y uy 的点 v v v。下面分情况讨论:

  1. v v v 无右子树,则将 u u u 添加到 v v v 的右子树上。
  2. v v v 有右子树,则将 v v v 的右子树添加到 u u u 的左子树,再将 u u u 添加到 v v v 的右子树。

特别的,如果 u u u 是最小的,那么将 u u u 设为根节点,其他节点放到 u u u 的左子树上。

时间复杂度 Θ ( n ) \Theta (n) Θ(n)


Part 3. 另一种做法

N_z_ 给出了一种带 log ⁡ \log log 的做法:最小值是根,左子树,右子树分别是 左半边,右半边 递归完成!

但可能会 TLE \text{TLE} TLE


Part 4. 代码实现

模板:洛谷 P5854

注意:时限 500ms \text{500ms} 500ms

Θ ( n ) \Theta (n) Θ(n) 做法:

#include 

using namespace std;

#define int long long
#define p 998244353
#define maxn 10000100

int a[maxn];
int st[maxn];
int ls[maxn],rs[maxn];

inline long long read()//快读
{
	long long x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	return x*f;
}

inline void write(long long x)//快输
{
	if(x<0)
	{
		putchar('-');
		x=-x;
	}
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n;n=read();
	int top(0);//卡常
	for (int i(1);i<=n;++i)
	{
		a[i]=read();
		int cnt=top;
		while (cnt&&a[st[cnt]]>a[i]) --cnt;//模仿单调栈,找第一个小于等于要插入节点的点
		if(cnt) rs[st[cnt]]=i;//直接插入
		if(cnt<top) ls[i]=st[cnt+1];//移一下
		st[++cnt]=i;
		top=cnt;
	}	
	int l(0),r(0);
	for (int i(1);i<=n;++i)
	{
		l^= i * (ls[i] + 1);//按题目要求来
		r^= i * (rs[i] + 1);
	}
	write(l);
	putchar(' ');
	write(r);
	return 0;
}

Part 5. 参考文献

  1. P5854
  2. 种树:二叉树、二叉搜索树、AVL树、红黑树、哈夫曼树、B树、树与森林

Part 6. 特别鸣谢

  1. @N_z_
  2. @linziyang1

你可能感兴趣的:(日常小知识,CSP冲刺,洛谷题解,学习,笔记,c++,数据结构)