首先显然应该把数组离散化,然后发现是个带修莫队裸题,但是求mex比较讨厌,怎么办?其实可以这样求:记录每个数出现的次数,以及出现次数的出现次数。至于求mex,直接暴力扫最小的出现次数的出现次数为0的正整数,就一句话,这样看似会超时,实际上是O(√n)的复杂度。为什么?假设存在出现1,2,...,x的出现次数,则Σi(1<=i<=x)<=n,即x*(x+1)<=2*n,所以x至多是√n级别。很多人再把出现次数分块,根本没必要。然后考虑把数组分块的块大小,每次移动左指针,为O(n*块大小),移动右指针,为O(n*块大小+n*块个数),移动修改标记,为O(n*块个数),然后显然块大小为O(n)最优,复杂度O(n)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+;
struct query{int l,r,t,id;}q[N];
struct mdf{int pos,pre,now;}d[N];
int n,m,B,l=,r,cnt,qcnt,mcnt,a[N],b[N],c[N],tmp[N],tot[N],num[N],ans[N];
bool cmp(query a,query b)
{
if((a.l-)/B!=(b.l-)/B)return a.l<b.l;
return(a.r-)/B==(b.r-)/B?a.t<b.t:a.r<b.r;
}
void add(int x,int v){tot[num[x]]--,num[x]+=v,tot[num[x]]++;}
void modify(int i,int f)
{
if(d[i].pos>=l&&d[i].pos<=r)add(a[d[i].pos],-);
a[d[i].pos]=f==?d[i].now:d[i].pre;
if(d[i].pos>=l&&d[i].pos<=r)add(a[d[i].pos],);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]),tmp[++cnt]=a[i];
for(int i=;i<=m;i++)
{
int op,x,y;scanf("%d%d%d",&op,&x,&y);
if(op==)q[++qcnt]=(query){x,y,mcnt,qcnt};
else d[++mcnt]=(mdf){x,,y},tmp[++cnt]=y;
}
sort(tmp+,tmp+cnt+);
cnt=unique(tmp+,tmp+cnt+)-tmp-;
for(int i=;i<=n;i++)a[i]=b[i]=lower_bound(tmp+,tmp+cnt+,a[i])-tmp;
for(int i=;i<=mcnt;i++)
{
d[i].now=lower_bound(tmp+,tmp+cnt+,d[i].now)-tmp;
d[i].pre=b[d[i].pos],b[d[i].pos]=d[i].now;
}
B=pow(n,0.67);
sort(q+,q+qcnt+,cmp);
for(int i=,tm=;i<=qcnt;i++)
{
while(tm<q[i].t)modify(++tm,);
while(tm>q[i].t)modify(tm--,-);
while(r<q[i].r)add(a[++r],);
while(l>q[i].l)add(a[--l],);
while(r>q[i].r)add(a[r--],-);
while(l<q[i].l)add(a[l++],-);
ans[q[i].id]=;
while(tot[ans[q[i].id]])ans[q[i].id]++;
}
for(int i=;i<=qcnt;i++)printf("%d\n",ans[i]);
}
05-11 22:08