笛卡爾樹

zxh923發表於2024-07-15

笛卡爾樹基本概念

笛卡爾樹是基於一個靜態序列 \(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;
} 

相關文章