Part 0. 闲话
nz 好闪,拜谢 nz
Part 1. 什么是笛卡尔树
定义:笛卡尔树是一种二叉树,每个节点有两个值 ( x i , y i ) (x_i,y_i) (xi,yi)。
特点:
-
如果只考虑 x i x_i xi ,那么笛卡尔树是一棵 二叉搜索树
-
如果只考虑 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。下面分情况讨论:
- v v v 无右子树,则将 u u u 添加到 v v v 的右子树上。
- 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 <bits/stdc++.h>
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;
}