首先我们注意到,对一个序列按分割点分开以后分别冒泡其实就相当于对整个序列进行冒泡。每一个元素都会对复杂度贡献1,除非一个元素两边的分割点都出现了。因此我们可以完全忽略快排的递归过程。只需考虑每个元素在经历几趟冒泡排序之后两边的分割点都出现了。
考虑一个分割点,不妨就是在i,i+1之间的这个分割点,他在几趟冒泡排序之后会出现呢?出现了这个分割点,说明前i小的元素都已经在前i个了,我们记前i小的元素一开始最大的下标为x,则在x-i趟冒泡之后就会把x挪到前i个位置(因为每趟冒泡一定会使x的下标-1)。也就是说i,i+1之间的分割点会在x-i趟冒泡之后出现。而第i个元素的贡献就是它左右分割点出现时间的最大值。
还要注意一些细节,比如某个元素两边的分割点出现时间为0,即一趟冒泡也没进行之前就已经排好了,但是我们的函数过程是一上来一定先冒泡一遍,因此也要贡献1.(当然,除非n=1,答案为0,直接退出)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
#define ll long long
#define inf 0x3f3f3f3f
inline char gc(){
static char buf[1<<16],*S,*T;
if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int n,cnt[N];ll ans=0;
struct node{
int id,x;
friend bool operator<(node a,node b){return a.x==b.x?a.id<b.id:a.x<b.x;}
}a[N];
int main(){
// freopen("a.in","r",stdin);
n=read();if(n==1){puts("0");return 0;}
for(int i=1;i<=n;++i) a[i].x=read(),a[i].id=i;
sort(a+1,a+n+1);int mx=0;
for(int i=1;i<n;++i){
mx=max(mx,a[i].id);
cnt[i]=mx-i;
}for(int i=1;i<=n;++i){
int x=max(cnt[i],cnt[i-1]);
if(!x) ++x;ans+=x;
}printf("%lld\n",ans);
return 0;
}