这个题用一波FHQ Treap做,不用STL了,正好检验一波手搓的无旋Treap怎么样。

题解:这个题插入操作跟普通的二叉树是相同的,让你找一个数的前序和后继,如果我们用普通的BST,势必会T到天上去,一条链足以卡飞BST,这里用了一波FHQ Treap无旋平衡树来写这个题。
很感谢这个题的输入的数据范围是大于1小于某个数(只知道小于1e6)(参考了zzugzx大佬的想法),所以就可以免去再做一写别的操作了。
我们设置一个最大值一个最小值,目的是为了保证他有前驱和后继,因为你插入一个值,会有4种情况。
1.前驱后继都有
2.有前驱没用后继
3.有后继没有前驱
4.没有后继也没有前驱
所以我们插入一个比较大的值和一个比较小的值,以确保每次插入的值都有前驱和后继,这样的话在我们每次找前驱后继都很方便了。
至于这个FHQ Treap的实现,代码中都写有注释了。

/*Keep on going Never give up*/
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
const int maxn = 11100;
const int MaxN = 0x3f3f3f3f;
const int MinN = 0xc0c0c00c;
typedef long long ll;
const int mod = 100000000;
using namespace std;

int cnt,root;
mt19937 rnd(233);  //随机数
struct Node{
    int l,r,val,key,size;
}fhq[1000010];
int newnode(int val){  //开辟新结点
    fhq[++cnt].val=val;
    fhq[cnt].key=rnd();
    fhq[cnt].size=1;
    return cnt;   //直接返回一个新结点
}

void update(int now){  //push_up更新节点大小(当加入了新结点)
    fhq[now].size=fhq[fhq[now].l].size+fhq[fhq[now].r].size+1;
}
void spilt(int now,int val,int &x,int &y){ //分裂乘两棵树
    if(!now) x=y=0;  //如果是一颗空树,直接返回x,y是空树即可
    else{
        if(fhq[now].val<=val){  //这里是按值分裂把小于等于val值得分到x树,大于val得分到y树
            x=now; //这个值左孩子上得所有结点都属于x这棵树
            spilt(fhq[now].r,val,fhq[now].r,y);
            //右孩子上大部分都会大于这个值,
            //也有可能会有小于val这个值得,所以我们要继续递归右子树进行查找
        }
        else{
            y=now;   //复读机
            spilt(fhq[now].l,val,x,fhq[now].l);
        }
        update(now);
    }
}
int merge(int x,int y){   //把两棵树合并起来
    if(!x||!y) return x+y;
    if(fhq[x].key>fhq[y].key){   //既要符合堆得性质,又要符合搜索树得性质
        //我们知道x这棵树上得值全都小于y这颗树上得值,
        // 所以当x得key大于y这个key得时候,y在x得右下方(既要在右边,也要在下面)
        //所以让x得右子树跟y合并
        fhq[x].r=merge(fhq[x].r,y);
        update(x);
        return x;
    }
    else{
        fhq[y].l=merge(x,fhq[y].l);
        update(y);
        return y;
    }
}
int x,y,z;
void insert(int val){  //插入某值
    spilt(root,val,x,y);  //先把树按照val值分裂开
    //因为x树都小于等于val,所以我们直接让新结点跟x合并起来
    //再把x树跟y树合并起来
    root=merge(merge(x,newnode(val)),y);
}
int pre(int val){   
    //找前驱,按照val-1分裂此树
    //在他前面的哪个数,一定是在x树上的最右端(最大值)
    spilt(root,val-1,x,y);
    int now=x;
    while (fhq[now].r) now=fhq[now].r;
    int temp=fhq[now].val;
    root=merge(x,y);
    return temp;
}
int nxt(int val){
    //找后继,按照val分裂此树
    //在他后面的树一定是y树上最左边的值(最小值)
    spilt(root,val,x,y);
    int now=y;
    while (fhq[now].l) now=fhq[now].l;
    int temp=fhq[now].val;
    root=merge(x,y);
    return temp;
}
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
int deep[1000010];
int main() {
    int n;
    cin>>n;
    insert(1000000),insert(0);
    deep[1000000]=deep[0]=-1;
    long long ans=0;
    while(n--){
        int v;
        v=read();
        deep[v]=max(deep[pre(v)],deep[nxt(v)])+1;
        insert(v);
        ans+=deep[v];
        printf("%lld\n",ans);
    }

    return 0;
}