本来是昨天的题,昨天补习了一下前置技能吉司机线段树
题意
给你一个1-n的排列,求最小的x个数构成的子序列的笛卡尔树的子树大小之和,x遍历1-n。
思路
考虑再原序列中插入下一个数带来的变化。观察笛卡尔树性质,每个结点对应一个区间,该节点子树大小等于区间长度。设maxr(l)表示所有以l为左端点的结点中r的最大值。对称的定义minl( r )。
关键:任意一个非根节点,minl( r ) = l 和maxr( l ) = r 有且只有一个成立,所以简单容斥一下发现答案 = sum(p(maxr(l))) + sum(p( r )) - sum(p(minl( r ))) - sum(p(l)) - n(n为总结点数,p为区间内节点数前缀和)
然后就是用吉司机线段树套树状数组维护这个和(两个树状数组,一个维护前缀和使用频率(即每个p(x)用了多少个),一个维护前缀和,吉司机线段树维护maxr(l),主要是区间和某个数取min和单点修改,总复杂度单log,每次线段树上修改时对频率树状数组做单点修改,总复杂度log^2)。乱七八糟的细节很多,但是样例很良心~~~~
值得一提的是先计算所有maxr(l) = r的结点,在计算剩下的结点时只需把整个序列翻转再跑一遍原来的程序即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 150505;
struct tree_array {
int c[maxn],tot;
inline void Add(int x,int val) {
if (!x)
return ;
for(int i = x;i <= tot;i += (i&-i)) c[i] += val;
}
int getsum(int st,int ed) {
int ret = 0;
for (int i = ed;i;i -= (i&-i)) ret += c[i];
if (st >= 0)
for (int i = st;i;i -= (i&-i)) ret -= c[i];
return ret;
}
inline void clr() {
tot = 0;
memset(c,0,sizeof(c));
}
}A,B;
int ch[maxn],df[maxn],n,a[maxn],p[maxn];
struct Node {
Node *l,*r;
int st,ed,mx,se,cnt,lazy;
}pool[303030],*root;
int z = 0,tot = 0;
inline void down(Node *now) {
now->l->mx = min(now->mx,now->l->mx);
now->r->mx = min(now->mx,now->r->mx);
}
inline void update(Node *now) {
if (now->l->mx == now->r->mx) {
now->cnt = now->l->cnt+now->r->cnt;
now->se = max(now->l->se,now->r->se);
now->mx = now->l->mx;
}
else if (now->l->mx > now->r->mx) {
now->mx = now->l->mx;
now->cnt = now->l->cnt;
now->se = max(now->r->mx,now->l->se);
}
else {
now->mx = now->r->mx;
now->cnt = now->r->cnt;
now->se = max(now->l->mx,now->r->se);
}
}
inline void Build(Node *&now,int st,int ed) {
now = &pool[++z];
now->st = st;
now->ed = ed;
if (st == ed) {
now->se = -1e9;
now->mx = 0;
now->cnt = 1;
return ;
}
Build(now->l,st,(st+ed)/2);
Build(now->r,(st+ed)/2+1,ed);
update(now);
}
inline void clr(Node *now) {
if (now->st == now->ed) {
now->se = -1e9;
now->mx = 0;
now->cnt = 1;
}
else {
clr(now->l);
clr(now->r);
update(now);
}
}
inline void ins(Node *now,int pos,int val) {
if (now->st > pos || now->ed < pos)
return ;
if (now->st == now->ed) {
ch[++tot] = now->mx;
df[tot] = -1;
now->mx = val;
ch[++tot] = now->mx;
df[tot] = 1;
return ;
}
down(now);
ins(now->l,pos,val);
ins(now->r,pos,val);
update(now);
}
inline void IntervalMin(Node *now,int st,int ed,int val) {
// cout << now->st << " " << now->ed << " " <<now->mx << " "<< now->cnt<<" "<<st << " " << ed <<" " << val << endl;
if (now->st > ed || now->ed < st || now->mx <= val)
return ;
if (now->st >= st && now->ed <= ed && now->se < val) {
ch[++tot] = now->mx;
df[tot] = -now->cnt;
now->mx = val;
ch[++tot] = now->mx;
df[tot] = now->cnt;
return ;
}
down(now);
IntervalMin(now->l,st,ed,val);
IntervalMin(now->r,st,ed,val);
update(now);
}
set<int> S,T;
long long ans[maxn] = {0};
inline void solve(int X) {
S.clear();
T.clear();
A.clr();
B.clr();
A.tot = B.tot = n;
if (X == 0)
Build(root,1,n);
else
clr(root);
S.insert(p[1]);
T.insert(p[1]-1);
A.Add(p[1],1);
B.Add(p[1]-1,-1);
B.Add(p[1],1);
int mx = p[1];
ins(root,p[1],p[1]);
long long tmp = 1;
ans[1] += 1;
set<int>::iterator it;
for (int i = 2;i <= n; i++) {
tot = 0;
A.Add(p[i],1);
tmp += B.getsum(p[i]-1,n);
it = S.lower_bound(p[i]);
if (it == S.end()) {
ins(root,*S.begin(),p[i]);
mx = p[i];
}
else if (it == S.begin()) {
ins(root,p[i],mx);
B.Add(p[i]-1,-1);
T.insert(p[i]-1); }
else {
ins(root,*it,mx);
if (T.find(*it-1) == T.end()) {
B.Add(*it-1,-1);
tmp -= A.getsum(0,*it-1);
T.insert(*it-1);
}
it--;
IntervalMin(root,1,p[i]-1,*it);
ins(root,*S.begin(),mx);
}
int ttt;
for (int j = 1;j <= tot; j++) {
ttt = A.getsum(0,ch[j]);
tmp += (long long)ttt*df[j];
B.Add(ch[j],df[j]);
}
S.insert(p[i]);
ans[i] += tmp;
}
}
int main() {
scanf("%d",&n);
for (int i = 1;i <= n; i++) {
scanf("%d",&a[i]);
p[a[i]] = i;
}
solve(0);
for (int i = 1;i <= n; i++) {
p[i] = n+1-p[i];
}
solve(1);
for (int i = 1;i <= n; i++) {
ans[i] -= i;
printf("%I64d\n",ans[i]);
}
return 0;
}