笛卡尔树

堆+BST

性质

  1. 笛卡尔树的子树是对应序列上一段连续的区间
    考虑 BST,最初 [1,n],考虑划分开 rt,分割成左右 2 个区间,且两个区间对应 2 棵子树,分治下去得证。

构造

考虑对于一个新节点 ai 的加入势必只会影响当前树的最右链,因为要满足下标 BST 性质。

然后既然已经满足了 BST 性质,不妨先假设我们维护的是个小根堆,即点的权值小于等于其子树内的所有权值。

考虑当前右链满足自父亲到儿子权值单增,那么显然你 a 的插入本质上就是抽离出来 ai 的底部链,并将这条链接到 a 的左儿子,这条链自身的结构并不需要改变,然后再将 ai 接到抽完的链的底部,作为其右儿子。

这个构造过程是很自然的,但为啥把这条链直接抽,并不需要改变其形态就是对的呢?

考虑这条链所挂着的其他儿子。

  • 堆性质,由于其最上的点满足 ai,所以其子树也一定满足。

  • BST,显然,你各个点的子树都是 BST,自然不需要更改。

https://acm.hdu.edu.cn/showproblem.php?pid=1506

#include <bits/stdc++.h> #define int long long #define pb push_back using namespace std; const int N=(int)(1e5+5); vector<int>g[N]; int n,a[N],sz[N],ls[N],rs[N],ans,tp,st[N],du[N]; void dfs(int x) { sz[x]=1; for(int y:g[x]) { dfs(y); sz[x]+=sz[y]; } ans=max(ans,sz[x]*a[x]); } void sol() { ans=-(int)(2e18); for(int i=1;i<=n;i++) { cin>>a[i]; ls[i]=rs[i]=du[i]=0; g[i].clear(); } tp=0; for(int i=1;i<=n;i++) { while(tp&&a[st[tp]]>a[i]) { ls[i]=st[tp]; --tp; } if(tp) rs[st[tp]]=i; st[++tp]=i; } for(int i=1;i<=n;i++) { if(ls[i]) g[i].pb(ls[i]),++du[ls[i]]; if(rs[i]) g[i].pb(rs[i]),++du[rs[i]]; } int rt=0; for(int i=1;i<=n;i++) { if(!du[i]) { rt=i; break ; } } dfs(rt); cout<<ans<<'\n'; } signed main() { cin.tie(0); ios::sync_with_stdio(false); while(1) { cin>>n; if(!n) return 0; sol(); } return 0; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/16883492.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示