题解:CF1290E Cartesian Tree
题意
给定一个
的排列 。 对于一个整数
,将排列中 的项构成的子序列建大根笛卡尔树。这棵笛卡尔树的所有节点的子树大小之和记为 。
, 。
分析
首先看到题目要求维护
考虑将
根据笛卡尔树的性质,我们知道
意味着
另一侧同理。
因为
答案为
我们考虑如何维护
这里以
先想一下子序列形成的过程,我们可以想成向一个序列中每次插入值,然后再查询。
有个显然的性质:每次插入的值是插入后序列的最大值。
我们将子序列沿插入位置
对于
所以
而对于
新插入的值作为序列的最大值,其子树就是整棵树,所以
所以我们只需要写一个数据结构支持以下四种操作:
- 单点插入一个值。
- 区间加。
- 区间取
或 。 - 查询和。
直接上平衡树。
写能维护
这时维护的就是
Code
#include<bits/stdc++.h> using namespace std; namespace Base_Treap { mt19937 rnd(time(0)); #define mx(x) (x?x->mx:-INT_MAX) #define smx(x) (x?x->smx:-INT_MAX) #define siz(x) (x?x->siz:0) struct node { uint32_t siz; uint64_t id; node *lc, *rc; int mx, smx, cnt, tag1, tag2, val; int64_t sum; node(int v, uint64_t idd) {sum=mx=val=v, lc=rc=0, siz=cnt=1, id=idd, smx=-INT_MAX, tag1=0; tag2=-INT_MAX;} void push_up() { sum=val;siz=1; if(lc) sum+=lc->sum, siz+=lc->siz; if(rc) sum+=rc->sum, siz+=rc->siz; mx=max({val, mx(lc), mx(rc)}); smx=max(smx(lc), smx(rc)); cnt=0; if(mx==val) cnt++; else smx=max(smx, val); if(mx==mx(lc)) cnt+=lc->cnt; else smx=max(smx, mx(lc)); if(mx==mx(rc)) cnt+=rc->cnt; else smx=max(smx, mx(rc)); } void add(int v) { sum+=1ll*v*siz; mx+=v; val+=v; if(smx!=-INT_MAX) smx+=v; if(tag2!=-INT_MAX) tag2+=v; tag1+=v; } void do_min(int v) { if(v>=mx) return; sum-=1ll*(mx-v)*cnt; val=min(val, v); tag2=mx=v; } void push_down() { if(tag1) { if(lc) lc->add(tag1); if(rc) rc->add(tag1); tag1=0; } if(tag2!=-INT_MAX) { if(lc) lc->do_min(tag2); if(rc) rc->do_min(tag2); tag2=-INT_MAX; } } void modify(int x) { if(mx<=x) return; if(smx<x) return do_min(x); push_down(); if(val>x) sum-=val-x, val=x; if(lc) lc->modify(x); if(rc) rc->modify(x); push_up(); } }; node* new_node(int x) {return new node(x, rnd());} void split(node *x, uint32_t k, node *&l, node *&r) { if(!x) return l=r=0, void(); x->push_down(); if(k>siz(x->lc)) l=x, split(x->rc, k-siz(x->lc)-1, x->rc, r); else r=x, split(x->lc, k, l, x->lc); x->push_up(); } node* merge(node *x, node *y) { if(!x||!y) return x?x:y; if(x->id>y->id) { x->push_down(); x->rc=merge(x->rc, y); x->push_up(); return x; } else { y->push_down(); y->lc=merge(x, y->lc); y->push_up(); return y; } } } using namespace Base_Treap; node *rtl=0, *rtr=0; void modify_r(int p, int v) { node *a, *b; split(rtr, p, a, b); if(a) a->modify(p); if(b) b->add(1); rtr=merge(a, merge(new_node(v), b)); } void modify_l(int p) { node *a, *b; split(rtl, p, a, b); if(b) b->add(-1), b->modify(-p-2); rtl=merge(a, merge(new_node(-1), b)); } int a[150005], pos[150005]; struct BIT: vector<int> { using vector<int>::vector; void modify(int p) {for(;p<size();p+=p&-p) at(p)++;} int query(int p) {int r=0;for(;p;p-=p&-p) r+=at(p); return r;} }ta(150005); int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i], pos[a[i]]=i; for(int i=1;i<=n;i++) { int np=ta.query(pos[i]); ta.modify(pos[i]); modify_l(np); modify_r(np, i); cout<<rtr->sum+rtl->sum+i<<'\n'; } }
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18379536
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步