笛卡尔树
选择数列里面最小的值作为根,从左边右边选择一个最小的值当做左右儿子,循环往复。拿最大值当做根也可以。主要取决于具体问题。
性质
1,区间最小(大)值。区间最小值就为区间左右端点值的LCA所代表的值。
就比如 3 5 1 7 4 6 2,那么5 6 之间的最小值就是5 与 6在树上的LCA,画个图可以看出是1.
2,中序遍历一遍就是原数组。
3,一个点的子树就是以这个点为最小值的极长的区间,因此一个数左边第一个比它小的数就是树上第一个左拐的位置。
构造
线段树构造的总复杂度是NlogN。每次查找最小值是logN。
ST表预处理NlognN。然后O(1)查找最小值。总复杂度是NlogN。
类单调栈方法总复杂度是N。
单调栈构造
特征:只有右半链会变,左半链一定不变。
int n;
int a[maxn],le[maxn],ri[maxn];
void build_dct(/* arguments */) {
stack<int> st;
int last=0,root=0;
for(int i=1;i<=n;i++) {
last=0;
while (!st.empty() && a[st.top()]>a[i]) {
last=st.top();
st.pop();
}
if(!st.empty()) ri[st.top()]=i;
else root=i;
le[i]=last;
st.push(i);
}
for(int i=1;i<=n;i++) {
printf("%d %d %d %d\n",i,a[i],le[i],ri[i] );
}
/* 输入内容
7
3 5 1 7 4 6 2
输出内容
1 3 0 2
2 5 0 0
3 1 1 7
4 7 0 0
5 4 4 6
6 6 0 0
7 2 5 0
*/
}
void init() {
n=rr();
for(int i=1;i<=n;i++) {
a[i]=rr();
}
}
int main(int argc, char const *argv[]) {
init();
build_dct();
return 0;
}
用处
求出所有区间的最小值。
序列结构转化成树形结构。
例题
给你一个1∼n的排列p1,p2,…,pn。
让你找到一个1∼n的排列q,满足对于任意区间[l,r](1≤l≤r≤n)满足pl,pl+1,…,pr中的最小值的位置,和ql,ql+1,…,qrr的最小值的位置相同。
输出满足条件的字典序最小的q。
输入格式
第一行一个整数n(1≤n≤106)。
接下来一行,一个长度为n的排列p1,p2,…,pn。
输出格式
一行nn个数q1,q2,…,qn,表示这个字典序最小的排列。
样例输入
5
4 3 1 5 2
样例输出
3 2 1 5 4
思路
由于笛卡尔树描述了序列数之间的关系,所以这里可以利用笛卡尔树。构出笛卡尔树之后,先序遍历一遍即可获得新的满足同样笛卡尔树关系的序列。
#include <bits/stdc++.h>
typedef long long ll;
#define pb push_back
#define pob pop_back
#define mem(a,b) memset(a,b,sizeof(a))
#define all(a) (a).begin(),(a).end()
#define debug(a) cout<<#a<<"="<<a<<endl;
inline ll rr(){ll f=1,x=0;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');return f*x;}
using namespace std;
const ll INF=0x3f3f3f3f,inf=0x3f3f3f3f3f3f3f;
const int maxn=1e6+6;
int n,tot;
int a[maxn],le[maxn],ri[maxn],ans[maxn];
void dfs(int u) {
if(!u) return ;
ans[u]=++tot;
dfs(le[u]);
dfs(ri[u]);
}
void build_dct(/* arguments */) {
stack<int> st;
int last=0,root=0;
for(int i=1;i<=n;i++) {
last=0;
while (!st.empty() && a[st.top()]>a[i]) {
last=st.top();
st.pop();
}
if(!st.empty()) ri[st.top()]=i;
else root=i;
le[i]=last;
st.push(i);
}
dfs(root);
}
void init() {
n=rr();
for(int i=1;i<=n;i++) {
a[i]=rr();
}
}
int main(int argc, char const *argv[]) {
init();
build_dct();
for(int i=1;i<=n;i++) {
std::cout << ans[i] << ' ';
}
std::cout << '\n';
return 0;
}