CF1290E Cartesian Tree

eee_hoho

2021-03-06 19:15:13

Solution

很套路的一道题。 先考虑对于一棵笛卡尔树如何求出所有子树大小,我们用单调栈建树的过程中是发现一个比当前栈顶元素大的数就弹栈,作为他的父亲,那么设 $pre_i$ 表示第一个向左比 $a_i$ 大的数的位置, $nxt_i$ 表示向右第一个比 $a_i$ 大的位置,那么 $i$ 的子树大小是 $nxt_i-pre_i-1$ 。 然后就可以分开考虑 $nxt$ 和 $pre$ 了,考虑求 $pre$ ,我们从小到大枚举 $k$ ,每次插入 $k$ ,设 $k$ 的位置是 $pos_k$ ,那么 $[1,pos_k-1]$ 的数的 $nxt$ 可能会改变,需要和 $pos_k'$ 取min( $pos_k'$ 是 $k$ 在新序列的位置),而 $k$ 的加入使 $[pos_k+1,n]$ 的数的位置加 $1$ ,所以 $nxt$ 要加 $1$ 。 这个东西可以用吉司机线段树来维护,至于插入数,我们多维护出这个区间有多少个数插入了就可以了。 对于 $pre$ ,直接把序列翻转,那么我们求的 $\sum pre$ 就变成了 $\sum i-pre+1$ , $i$ 是序列元素个数,最后把多算的减掉就好了。 **Code** ``` cpp #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> const int N = 2e5; const int inf = 1e9; using namespace std; int n,a[N + 5],pos[N + 5],s[N + 5]; long long ans[N + 5]; struct node { int mx,mxi,smx,s; long long sm; node operator +(const node &a)const { node c = {0,0,0,0,0}; c.sm = sm + a.sm; c.s = s + a.s; if (mx > a.mx) { c.mx = mx; c.smx = smx; c.mxi = max(a.mx,mxi); } if (mx < a.mx) { c.mx = a.mx; c.smx = a.smx; c.mxi = max(a.mxi,mx); } if (mx == a.mx) { c.mx = mx; c.smx = a.smx + smx; c.mxi = max(a.mxi,mxi); } return c; } }; struct Seg { node s[N * 4 + 5]; int tag[N * 4 + 5],ct[N * 4 + 5]; #define zrt k << 1 #define yrt k << 1 | 1 void build(int k,int l,int r) { s[k] = {0,-inf,0,0,0};tag[k] = 0;ct[k] = inf; if (l == r) return; int mid = l + r >> 1; build(zrt,l,mid); build(yrt,mid + 1,r); } void add(int k,int l,int r,int z) { if (!s[k].s) return; s[k].sm += 1ll * s[k].s * z; s[k].mx += z; if (s[k].mxi != -inf) s[k].mxi += z; tag[k] += z; if (ct[k] != inf) ct[k] += z; } void cha(int k,int z) { if (!s[k].s) return; if (z >= s[k].mx) return; s[k].sm -= 1ll * s[k].smx * (s[k].mx - z); s[k].mx = z; ct[k] = z; } void pushdown(int k,int l,int r,int mid) { if (tag[k]) { add(zrt,l,mid,tag[k]); add(yrt,mid + 1,r,tag[k]); tag[k] = 0; } if (ct[k] != inf) { cha(zrt,ct[k]); cha(yrt,ct[k]); ct[k] = inf; } } void insert(int k,int l,int r,int x,int z) { if (l == r) { s[k] = (node){z,-inf,1,1,z}; return; } int mid = l + r >> 1; pushdown(k,l,r,mid); if (x <= mid) insert(zrt,l,mid,x,z); else insert(yrt,mid + 1,r,x,z); s[k] = s[zrt] + s[yrt]; } void modify(int k,int l,int r,int x,int y,int z) { if (l >= x && r <= y) { add(k,l,r,z); return; } int mid = l + r >> 1; pushdown(k,l,r,mid); if (x <= mid) modify(zrt,l,mid,x,y,z); if (y > mid) modify(yrt,mid + 1,r,x,y,z); s[k] = s[zrt] + s[yrt]; } void getmin(int k,int l,int r,int x,int y,int z) { if (l >= x && r <= y) { if (z > s[k].mxi) { cha(k,z); return; } } int mid = l + r >> 1; pushdown(k,l,r,mid); if (x <= mid) getmin(zrt,l,mid,x,y,z); if (y > mid) getmin(yrt,mid + 1,r,x,y,z); s[k] = s[zrt] + s[yrt]; } int query(int k,int l,int r,int x,int y) { if (l >= x && r <= y) return s[k].s; int mid = l + r >> 1; pushdown(k,l,r,mid); if (y <= mid) return query(zrt,l,mid,x,y); if (x > mid) return query(yrt,mid + 1,r,x,y); return query(zrt,l,mid,x,y) + query(yrt,mid + 1,r,x,y); } }tree; void solve() { for (int i = 1;i <= n;i++) pos[a[i]] = i; tree.build(1,1,n); memset(s,0,sizeof(s)); for (int k = 1;k <= n;k++) { if (pos[k] + 1 <= n) tree.modify(1,1,n,pos[k] + 1,n,1); if (pos[k] - 1 >= 1) { int x = tree.query(1,1,n,1,pos[k] - 1) + 1; tree.getmin(1,1,n,1,pos[k] - 1,x); } tree.insert(1,1,n,pos[k],k + 1); ans[k] += tree.s[1].sm; } } int main() { scanf("%d",&n); for (int i = 1;i <= n;i++) scanf("%d",&a[i]); solve(); reverse(a + 1,a + n + 1); solve(); for (int i = 1;i <= n;i++) printf("%lld\n",ans[i] - 1ll * i * (i + 2)); return 0; } ```