笛卡爾樹基本概念
笛卡爾樹是基於一個靜態序列 \(a\) 的,根據這個序列 \(a\),我們可以構造出對應的笛卡爾樹。
笛卡爾樹有三點要求需要滿足:
-
笛卡爾樹是二叉樹。
-
笛卡爾樹的編號的中序遍歷為 \(1\sim n\),權值中序遍歷為 \(a\)。
-
笛卡爾樹的權值滿足大根堆或者小根堆的性質。
這裡可以看一個圖,節點上標的是權值。
為了更加方便,笛卡爾樹的節點編號與序列 \(a\) 的下標編號共用。
笛卡爾樹的構造
這裡我們先給出笛卡爾樹的性質,這裡以小根笛卡爾樹為例:
-
對於樹上一條深度單調遞增的路徑,其權值單調不減。
-
\(\forall u,v;u\le v;p=\operatorname{lca}(u,v)=p\),以 \(p\) 為根的子樹恰好涵蓋 \(a\) 序列中 \([u,v]\) 區間的所有位置。
-
\(\forall u,v;u\le v;p=\operatorname{lca}(u,v)=p\),其在笛卡爾樹上的最近公共祖先的權值 \(val_p\) 恰好為 \(a\) 序列中 \([u,v]\) 區間的最小值。
於是我們可以線上性時空內構造笛卡爾樹。
我們考慮維護笛卡爾樹上最右端的鏈,則有這條鏈的編號和權值都不減,於是我們可以用單調棧維護。
我們從前向後遍歷 \(a\) 陣列,設當前的位置為 \(i\),同時前 \(i-1\) 位的笛卡爾樹已經構建完成,於是我們假設當前棧頂位置 \(top\),則當前棧頂元素為 \(s_{top}\),然後我們進行分類討論。
-
\(a_i\ge a_{s_{top}}\),直接將 \(i\) 入棧。事實上就是在笛卡爾樹的最右端的最下面插入這個元素。
-
否則,開始彈出棧,直到棧為空或者棧頂元素權值不小於 \(a_i\),這裡設最後一個彈出的元素的下標設為 \(j\)。則把 \(i\) 入棧,\(j\) 變為 \(i\) 的左兒子。
這裡再掛個圖,方便理解:
然後我們就建完樹了,可以開始看題了。
笛卡爾樹
考慮直接按照上面的步驟建樹,維護一下左右兒子即可,沒有需要特別注意的地方,所以直接放一下程式碼:
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define x first
#define y second
#define N 10000005
using namespace std;
int n,a[N],stk[N],rt,res1,res2;
pii w[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int top=0,cur=0;
for(int i=1;i<=n;i++){
cur=top;
while(top&&a[stk[cur]]>a[i])cur--;//彈棧
if(cur)w[stk[cur]].y=i;//沒彈完代表有東西更小,作為右兒子
if(cur<top)w[i].x=stk[cur+1];//有東西出棧了,把出棧的最後一個節點作為當前點左兒子
stk[++cur]=i;//入棧
top=cur;
}
for(int i=1;i<=n;i++){
res1^=(i*(w[i].x+1));
res2^=(i*(w[i].y+1));
}
cout<<res1<<' '<<res2;
return 0;
}
樹的序
類似於板子,說了這麼多,其實就是對笛卡爾樹求一個前序遍歷,所以直接上程式碼:
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define x first
#define y second
#define N 10000005
using namespace std;
int n,a[N],stk[N],rt,res1,res2;
pii w[N];
void dfs(int u){
if(u==0)return;
cout<<u<<' ';
dfs(w[u].x);
dfs(w[u].y);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
a[x]=i;
}
int top=0,cur=0;
for(int i=1;i<=n;i++){
cur=top;
while(top&&a[stk[cur]]>a[i])cur--;
if(cur)w[stk[cur]].y=i;
if(cur<top)w[i].x=stk[cur+1];
stk[++cur]=i;
top=cur;
}
dfs(stk[1]);
return 0;
}