笛卡尔树

https://www.luogu.com.cn/problem/P5854

题意概述

给定一个排列 \(p\),构建其笛卡尔树,即:

  • 每个节点编号满足二叉搜索树的性质。

  • 每个节点的权值满足小根堆的性质。

思路

使用单调栈。对于节点 \(i\),先弹出单调栈中所有值大于 \(p_i\) 的,记录最后一个弹出的编号 \(last\)。如果栈非空,节点 \(i\) 成为栈顶元素的右孩子,如果栈顶元素已经有右孩子(\(last\) 不为 \(0\)),原本的右孩子成为 \(i\) 的左孩子;如果栈空,节点 \(i\) 成为头节点,此时原本的头节点(如果有)为 \(last\),成为 \(i\) 的左孩子。

时间复杂度 \(\mathcal{O}(n)\)

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;


void solve(){
	int n;
	cin >> n;

	vector<int> p(n+1);
	for (int i=1;i<=n;i++){
		cin >> p[i];
	}

	stack<int> stk;
	vector<int> left(n+1,-1),right(n+1,-1);
	for (int i=1;i<=n;i++){
		int last = -1;
		while (!stk.empty() && p[i]<=p[stk.top()]){
			last = stk.top();
			stk.pop();
		}

		if (last!=-1){
			left[i] = last;
		}
		if (!stk.empty()){
			right[stk.top()] = i;			
		}
		stk.push(i);
	}

	ll res1=0,res2=0;
	for (int i=1;i<=n;i++){
		res1^=(ll)i*((left[i]==-1?0:left[i])+1);
		res2^=(ll)i*((right[i]==-1?0:right[i])+1);
	}
	cout << res1 << ' ' << res2 << '\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-16 13:38  kzssCCC  阅读(12)  评论(0)    收藏  举报