题解:CF1290E Cartesian Tree
题意
给定一个 \(1\sim n\) 的排列 \(a\)。
对于一个整数 \(k\in[1,n]\),将排列中 \(\leqslant k\) 的项构成的子序列建大根笛卡尔树。这棵笛卡尔树的所有节点的子树大小之和记为 \(s_k\)。
\(\forall k\in[1,n]\),\(s_k\)。
分析
首先看到题目要求维护 \(s_k\),但是这个东西和笛卡尔树的性质没什么关系。
考虑将 \(s_k\) 拆成多个信息。
根据笛卡尔树的性质,我们知道 \(s_i\) 就是子序列上以 \(i\) 为最大值的极大区间 \([l_i, r_i]\) 的长度。
意味着 \(l_i-1\) 等于 \(0\) 或其权值 \(val_{l_i-1}>i\)。
另一侧同理。
因为 \(s_i=r_i-l_i+1\),所以我们维护左右端点 \(l_i,r_i\)。
答案为 \(\sum_{i=1}^k\limits s_i = \sum_{i=1}^k\limits r_i - l_i + 1\)。
我们考虑如何维护 \(l_i\) 和 \(r_i\)。
这里以 \(r_i\) 为例。
先想一下子序列形成的过程,我们可以想成向一个序列中每次插入值,然后再查询。
有个显然的性质:每次插入的值是插入后序列的最大值。
我们将子序列沿插入位置 \(p\) 拆成两个区间 \([1,p-1]\) 和 \([p+1, k]\)。
对于 \(i \in [1, p-1]\),因为值一定小于新插入的值,所以其右端点 \(r_i\) 一定也在区间 \([1,p-1]\) 中。
所以 \(r_i \gets \min(r_i,p-1)\)。
而对于 \(i \in [p+1, k]\),它们的 \(r_i\) 和新插入的值无关,只是增加了 \(1\)。
\(l_i\) 的想法差不多,二者维护方式如下:
新插入的值作为序列的最大值,其子树就是整棵树,所以 \(l_p \gets 1,r_p\gets k\)。
所以我们只需要写一个数据结构支持以下四种操作:
- 单点插入一个值。
- 区间加。
- 区间取 \(\min\) 或 \(\max\)。
- 查询和。
直接上平衡树。
写能维护 \(\min\) 和 \(\max\) 的平衡树码量有点大,可以只写能维护 \(\min\) 的平衡树。
这时维护的就是 \(r_i\) 和 \(-l_i\)。
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';
}
}

浙公网安备 33010602011771号