给一个长度为n的数列a,q个询问,每次询问一段区间的mex。(没有出现过的最小非负整数)
1<=n,q<=200000,0<=ai<=200000。
题解1 莫队
我们将权值分成根号块,记录每个权值的出现次数和每块内有多少权值出现过。
修改和询问就直接暴力做就行。
修改O(1),询问O(sqrt(n)),加在一起还是O((n+q)sqrt(n+q))。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define SZ 666666
int n,q,a[SZ],ts[SZ],tc[SZ],gg,bk,anss[SZ];
void edt(int p,int x)
{
tc[p/gg]-=(bool)ts[p];
ts[p]=x;
tc[p/gg]+=(bool)ts[p];
}
struct query {int l,r,id;} qs[SZ];
bool operator < (query a,query b)
{
int ap=a.l/bk, bp=b.l/bk;
if(ap==bp) return a.r<b.r;
return ap<bp;
}
void add(int p) {edt(p,ts[p]+1);}
void del(int p) {edt(p,ts[p]-1);}
#define gc getchar()
int g_i()
{
int tmp=0; bool fu=0; char s;
while(s=gc,s!='-'&&(s<'0'||s>'9')) ;
if(s=='-') fu=1; else tmp=s-'0';
while(s=gc,s>='0'&&s<='9') tmp=tmp*10+s-'0';
if(fu) return -tmp; else return tmp;
}
#define gi g_i()
#define pob
#define pc(x) putchar(x)
namespace ib {char b[100];}
inline void pint(int x)
{
if(x==0) {pc(48); return;}
if(x<0) {pc('-'); x=-x;}
char *s=ib::b;
while(x) *(++s)=x%10, x/=10;
while(s!=ib::b) pc((*(s--))+48);
}
int main()
{
n=gi, q=gi;
for(int i=1;i<=n;i++) a[i]=gi;
gg=sqrt(200000);
bk=min(int(sqrt(n)+1),n);
for(int i=1;i<=q;i++) qs[i].id=i, qs[i].l=gi, qs[i].r=gi;
sort(qs+1,qs+1+q);
int cl=1,cr=0;
for(int i=1;i<=q;i++)
{
int l=qs[i].l,r=qs[i].r;
while(cr<r) add(a[++cr]);
while(cr>r) del(a[cr--]);
while(cl>l) add(a[--cl]);
while(cl<l) del(a[cl++]);
int nof=0;
for(int j=0;;j++)
{
if(tc[j]!=gg) {nof=j; break;}
}
for(int j=nof*gg;;j++)
{
if(!ts[j]) {anss[qs[i].id]=j; break;}
}
}
for(int i=1;i<=q;i++) {pint(anss[i]); pc(10);}
}
题解2 线段树
我们这么考虑,从小到大加入所有权值。如果有一个权值正好没有经过,这个权值就可以作为答案,就可以退出了。
现在我们考虑对于一个权值,所有不包括它的区间都可以以它作为答案,那么我们就把同样是这个权值的搞出来,那么这个序列就会被搞成几段,那么每一段内的询问都可以更新答案。
那么我们就二维线段树即可。我tm才不想写