Description

【NOIP提高】自然数_NOIP

Solution

mex是清华夏令营的题目,这道NOIP的题绝对比那道题难。

用50分预处理一个东西

设b[i]=mex(1,i)。
先用50分求出b数组。

推广

我们试着把mex(1,i)推到mex(2,i)。
那么我们需要删掉a[1]这个值。
假设右边第一个等于a[1]的位置是rr[1]。
我们考虑一下有影响的区间,假如第一个等于a[1]的位置是x(就是a[1]=a[x]),那么删掉了a[1]对mex(1,x…n)都没有影响,但是mex(1,1…x-1)可能会有影响。
因为mex(1,1…n)一定是单调递增的,所以删掉了a[1]会让第一个大于a[1]的mex的位置之后的那些mex受影响。
设第一个大于a[1]的mex的位置是o,那么mex(1,o…rr[1])的值都会被改为a[1]。
这些东西用线段树就可以解决了。
那么一次只用删掉a[1…n-1]。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=200007,mo=1000007;
int i,j,k,l,n,m;
int a[maxn],b[maxn],rr[maxn],hh[mo],wei[mo];
bool bz[maxn];
long long ans,ans1;
map<int,int>h;
struct node{
int da,biao;
long long sum;
}t[maxn*10];
void merge(int x){
t[x].sum=t[x*2].sum+t[x*2+1].sum;
t[x].da=max(t[x*2].da,t[x*2+1].da);
}
void down(int x,int l,int r){
int mid=(l+r)/2;
if(t[x].biao!=-1){
t[x*2].biao=t[x*2+1].biao=t[x].biao;
t[x*2].sum=(mid-l+1)*t[x].biao;
t[x*2+1].sum=(r-mid)*t[x].biao;
t[x*2].da=t[x*2+1].da=t[x].biao;
t[x].biao=-1;
}
}
void build(int x,int l,int r){
t[x].biao=-1;
if(l==r){
t[x].sum=t[x].da=b[l];
return;
}
int mid=(l+r)/2;
build(x*2,l,mid);
build(x*2+1,mid+1,r);
merge(x);
}
int find(int x,int l,int r,int y){
if(l==r){
if(t[x].sum<=y)return n+1;
return l;
}
down(x,l,r);
int mid=(l+r)/2;
if(t[x*2].da<=y)return find(x*2+1,mid+1,r,y);
else return find(x*2,l,mid,y);
}
int hash(int x){
int o=x%mo;
while(hh[o]!=x&&hh[o]!=0)o=(o+1)%mo;
return o;
}
void change(int x,int l,int r,int y,int z,int o){
if(y>z)return;
down(x,l,r);
if(l==y&&r==z){
t[x].biao=t[x].da=o;
t[x].sum=(r-l+1)*o;
return;
}
int mid=(l+r)/2;
if(z<=mid)change(x*2,l,mid,y,z,o);
else if(mid<y)change(x*2+1,mid+1,r,y,z,o);
else{
change(x*2,l,mid,y,mid,o);
change(x*2+1,mid+1,r,mid+1,z,o);
}
merge(x);
}
int main(){
freopen("fan.in","r",stdin);
scanf("%d",&n);
fo(i,1,n)scanf("%d",&a[i]);
fod(i,n,1){
int u=hash(a[i]);
rr[i]=wei[u];
wei[u]=i;
hh[u]=a[i];
}
fo(i,1,n){
h[a[i]]=1;
while(h[ans1]!=0)ans1++;
ans+=ans1;
b[i]=ans1;
}
build(1,1,n);
fo(i,1,n-1){
int o=find(1,1,n,a[i]);
if(rr[i])change(1,1,n,o,rr[i]-1,a[i]);
else change(1,1,n,o,n,a[i]);
ans+=t[1].sum;
}

printf("%lld\n",ans);
}