题解:CF1290E Cartesian Tree

题意

给定一个 1n 的排列 a

对于一个整数 k[1,n],将排列中 k 的项构成的子序列建大根笛卡尔树。这棵笛卡尔树的所有节点的子树大小之和记为 sk

k[1,n]sk

分析

首先看到题目要求维护 sk,但是这个东西和笛卡尔树的性质没什么关系。

考虑将 sk 拆成多个信息。

根据笛卡尔树的性质,我们知道 si 就是子序列上以 i 为最大值的极大区间 [li,ri] 的长度。

意味着 li1 等于 0 或其权值 valli1>i

另一侧同理。

因为 si=rili+1,所以我们维护左右端点 li,ri

答案为 i=1ksi=i=1krili+1


我们考虑如何维护 liri

这里以 ri 为例。

先想一下子序列形成的过程,我们可以想成向一个序列中每次插入值,然后再查询。

有个显然的性质:每次插入的值是插入后序列的最大值。

我们将子序列沿插入位置 p 拆成两个区间 [1,p1][p+1,k]

对于 i[1,p1],因为值一定小于新插入的值,所以其右端点 ri 一定也在区间 [1,p1] 中。

所以 rimin(ri,p1)

而对于 i[p+1,k],它们的 ri 和新插入的值无关,只是增加了 1

li 的想法差不多,二者维护方式如下:

(1)ri{min(ri,p1)i[1,p1]ri+1i[p+1,k](2)li{lii[1,p1]max(p+1,li+1)i[p+1,k]

新插入的值作为序列的最大值,其子树就是整棵树,所以 lp1,rpk


所以我们只需要写一个数据结构支持以下四种操作:

  • 单点插入一个值。
  • 区间加。
  • 区间取 minmax
  • 查询和。

直接上平衡树。


写能维护 minmax 的平衡树码量有点大,可以只写能维护 min 的平衡树。

这时维护的就是 rili

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 中国大陆许可协议进行许可。

posted @   Jimmy-LEEE  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起