1. 程式人生 > 其它 >洛谷 P5854 【模板】笛卡爾樹

洛谷 P5854 【模板】笛卡爾樹

傳送門


前置知識

單調棧

笛卡爾樹

定義

每個節點都由一個鍵值二元組構成(x,y)。
要求構建一棵樹,滿足:

  1. x上是一個二叉搜尋樹(左兒子<根<右兒子)
  2. y上是一個小根堆

構建過程

於是我們可以按照x值從小到大將節點加入笛卡爾樹中。
新加入的點now一定要放在某個節點的右兒子上(或者根節點)(因為需要滿足條件1)
然後我們從根節點一直向右走,遇到的第一個y值大於now的y值的點,就不能往下走了(因為需要滿足條件2),這時將這個節點設定成now的左兒子,把now代替掉這個節點的位置,就能滿足要求。
這個過程可以維護一個單調遞增棧來記錄這個從根節點一直往右走的路徑。

性質與應用

當節點的x,y都給定且不相同時,笛卡爾樹是確定唯一的。
可以用來處理區間RQM,兩個節點的LCA即為答案。
還有什麼結合+1/-1 RMQ線上解決RQM問題的我根本不會

AC程式碼

#include<cstdio>
#include<iostream>
#include<stack>
using namespace std;
const int maxn=1e7+5;
int n,a[maxn];
long long l[maxn],r[maxn],ans1,ans2;
stack<int> s;
inline char rc()
{
	static char buf[1000000],*pn=buf,*pe=buf;
	return (pn==pe)&&(pe=(pn=buf)+fread(buf,1,1000000,stdin),pn==pe)?EOF:*pn++;
}
inline int read()
{
	int f=0;
	char cc=rc();
	while(cc<'0'||cc>'9')cc=rc();
	while(cc>='0'&&cc<='9')f=f*10+cc-'0',cc=rc();
	return f;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		a[i]=read();
		while(!s.empty()&&a[s.top()]>a[i]) l[i]=s.top(),s.pop();
		if(!s.empty()) r[s.top()]=i;
		s.push(i);
	}
	for(int i=1;i<=n;i++) ans1=ans1^(i*(l[i]+1)),ans2=ans2^(i*(r[i]+1));
	printf("%lld %lld",ans1,ans2);
	return 0;
}