概念
笛卡尔树的节点各有两个权值,其中一个权值满足二叉搜索树的性质,另一个满足小(大)根堆的性质。
所以说 Treap 也是一种笛卡尔树。
构造
以下均假设原序列元素两两不相同。
从左至右依次加入元素,维护当前笛卡尔树的右儿子链
\[root\to rson\to rson.rson\to rson.rson.rson\dots \]至一个栈内。
设加入的节点为 \(x\),\(t\) 为当前笛卡尔树。
-
找到栈中最靠顶部的节点 \(y\) 使得 \(val[y]<val[x]\)。
-
特判 \(y\) 为栈顶,直接 \(t[y].rson:=x\) 即可结束。
-
记栈中 \(y\) 的楼上为 \(z\),\(t[y].rson:=x,t[x].lson:=z\)。
-
将栈中 \(y\) 之上的所有全部 \(pop\),再 \(push(x)\)。
时间 \(O(n)\)
实战
模板
好像单调栈能做的,她都行。
以前这里贴了一个 \(O(n\log n)\) 的代码,放了好久,我该咋办。
现在改好了,\(O(n)\) 的。
/*
* Author: ShaoJia
* Last Modified time: 2022-09-27 18:20:41
* Motto: We'll be counting stars.
*/
#include<bits/stdc++.h>
using namespace std;
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define ll long long
#define N 10000005
char buf[1<<21],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read() {
int x=0,f=1;
char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=gc();}
return x*f;
}
int n,a[N],ls[N],rs[N],s[N],st;
signed main(){
n=read();
For(i,1,n) a[i]=read();
s[st=1]=1;
int x;
For(i,2,n){
if(a[i]>a[s[st]]){
rs[s[st]]=i;
s[++st]=i;
}else if(a[i]<a[s[1]]){
ls[i]=s[1];
s[st=1]=i;
}else{
x=s[st--];
while(a[s[st]]>a[i]){
x=s[st--];
}
rs[s[st]]=i;
ls[i]=x;
s[++st]=i;
}
}
// For(i,1,n) cout<<ls[i]<<" "; cout<<"\n";
// For(i,1,n) cout<<rs[i]<<" "; cout<<"\n";
ll L=0,R=0;
For(i,1,n){
L^=1ll*i*(ls[i]+1);
R^=1ll*i*(rs[i]+1);
}
cout<<L<<" "<<R<<"\n";
return 0;}
CF1220F Gardener Alex
vp 场上 \(O(n)\) 想法因为 sb 错误没调出来,被 Little09 \(O(n\log n)\) 压哨过暴踩。
发现其实就是要快速求出一个序列的任意前缀的笛卡尔树的深度。
发现我们可以直接在右链上维护每个节点左儿子的 \(\text{mxdep}\),压栈和弹栈的时候维护一下即可。
细节看代码吧,讲不清楚 /yun。
P5654 基础函数练习题
咕咕咕,我还没 A 这道题呢。
标签:rs,rson,笛卡尔,st,权值,节点 From: https://www.cnblogs.com/shaojia/p/15415540.html