Description
Input
第一行n
第二行n个数
Output
答案
Sample Input
3
0 1 3
Sample Output
5
Data Constraint
n<=200000
ai<=109
Solution
50分暴力明显,用标记数组随便YY
但是你可能在想要离散化,其实凡是大于n的都直接等于n就行了,mex最大就是n
想:如果知道[1,n]的mex,能不能求到[2,n]
显然mex[1,i]是单调递增的,当第一个数被删掉时,如果后面原来比这个数大,并且在下一个数出现之前,那就变成这个数
区间修改就可以用线段树了
加上一个线段树上二分
线段树初始化为mex[1,1],mex[1,2],mex[1,3]……一次修改变成mex[2,2],mex[2,3]……
修改n-1次,每次求和就行了
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 201000
#define ll long long
using namespace std;
int n,a[N],tot=0,bz[N],next[N];
ll ans=0;
struct note{
ll m,h,l;
};
note t[N*25];
void down(int v,int i,int j)
{
if(t[v].l==-1) return;
int m=(i+j)/2;
t[v*2].m=t[v*2+1].m=t[v*2+1].l=t[v*2].l=t[v].l;
t[v*2].h=t[v].l*(m-i+1);t[v*2+1].h=t[v].l*(j-m);t[v].l=-1;
}
void insert(int v,int i,int j,int x,int y,ll z)
{
if(i==x&&j==y){t[v].l=t[v].m=z;t[v].h=z*(y-x+1);return;}
int m=(i+j)/2;down(v,i,j);
if(y<=m) insert(v*2,i,m,x,y,z);
else if(x>m) insert(v*2+1,m+1,j,x,y,z);
else insert(v*2,i,m,x,m,z),insert(v*2+1,m+1,j,m+1,y,z);
t[v].m=max(t[v*2].m,t[v*2+1].m);t[v].h=t[v*2].h+t[v*2+1].h;
}
int find(int v,int i,int j,ll x)
{
if(i==j){return i;}
int m=(i+j)/2;down(v,i,j);
if(t[v*2].m>x) return find(v*2,i,m,x);
else return find(v*2+1,m+1,j,x);
}
int main()
{
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
//-----------------------------
memset(bz,0,sizeof(bz));
int k=0;
fo(j,1,n)
{
int i=a[j]>n?n:a[j];
if(bz[i]==0) bz[i]=j;
else next[bz[i]]=j,bz[i]=j;
while (bz[k]>0) k++;
ans+=(ll)k;insert(1,1,n,j,j,k);
}
fo(i,1,n) next[i]=(next[i]==0)?n+1:next[i];
fo(i,1,n-1){
int k=find(1,1,n,a[i]);
if(t[1].m<a[i]) k=n+1;
if (next[i]-1>=k) insert(1,1,n,k,next[i]-1,a[i]);
insert(1,1,n,i,i,0);
ans+=t[1].h;
}
printf("%lld",ans);
}