笛卡尔树笔记
【模板】笛卡尔树
题目描述
给定一个 \(1 \sim n\) 的排列 \(p\),构建其笛卡尔树。
即构建一棵二叉树,满足:
- 每个节点的编号满足二叉搜索树的性质。
- 节点 \(i\) 的权值为 \(p_i\),每个节点的权值满足小根堆的性质。
输入格式
第一行一个整数 \(n\)。
第二行一个排列 \(p_{1 \dots n}\)。
输出格式
设 \(l_i,r_i\) 分别表示节点 \(i\) 的左右儿子的编号(若不存在则为 \(0\))。
一行两个整数,分别表示 \(\operatorname{xor}_{i = 1}^n i \times (l_i + 1)\) 和 \(\operatorname{xor}_{i = 1}^n i \times (r_i + 1)\)。
样例 #1
样例输入 #1
5
4 1 3 2 5
样例输出 #1
19 21
提示
【样例解释】
\(i\) | \(l_i\) | \(r_i\) |
---|---|---|
\(1\) | \(0\) | \(0\) |
\(2\) | \(1\) | \(4\) |
\(3\) | \(0\) | \(0\) |
\(4\) | \(3\) | \(5\) |
\(5\) | \(0\) | \(0\) |
【数据范围】
对于 \(30\%\) 的数据,\(n \le 10^3\)。
对于 \(60\%\) 的数据,\(n \le 10^5\)。
对于 \(80\%\) 的数据,\(n \le 10^6\)。
对于 \(90\%\) 的数据,\(n \le 5 \times 10^6\)。
对于 \(100\%\) 的数据,\(1 \le n \le 10^7\)。
思路
以当前为根,从左往右走,如果能放在当前右子树,否则就一直往上找,直到找到当前的权值 \(p_i\) 比当前点的权值 \(p_i\) 大,则符合条件,放在其右子树。对于向上找时跳过的不符合条件的点,则将其放在当前接入点的左儿子。
那么,这样子的复杂度就是 \(O(N^2)\) 。考虑将向上遍历直到找到当前的权值 \(p_i\) 比当前点的权值 \(p_i\) 大这一步骤优化,即找到先前序列中位于该位置之前的以一个大于该值的树。由此想到了单调栈。
所以做法大致是“贪心 \(+\) 单调栈”。
code
注意要输入解绑,不然会 \(TLE\) (实践出真知)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e7+5;
int st[N];
int a[N];
int n;
int l[N],r[N];
void build(){
int top=0,cur=0;
for(int i=1;i<=n;i++){
while(cur&&a[st[cur]]>a[i]) cur--;
//cur:插在某个结点的右子树上
if(cur) r[st[cur]]=i;
//cur<top:插在某个结点的左子树上
if(cur<top) l[i]=st[cur+1];
st[++cur]=i;
top=cur;
}
}
signed main(){
ios::sync_with_stdio(NULL);
cin.tie(NULL);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
build();
int ans=0;
for(int i=1;i<=n;i++) ans^=1LL*i*(l[i]+1);
cout<<ans<<" ";
ans=0;
for(int i=1;i<=n;i++) ans^=1LL*i*(r[i]+1);
cout<<ans<<endl;
return 0;
}
标签:10,le,cur,笛卡尔,int,笔记,权值
From: https://www.cnblogs.com/yingxilin/p/18620030