题意:
给出一个长度为n的自然数序列,m次查询某一区间[l,r]中数集的mex函数值;
n,m<=200000,0<=a[i]<=10^9;
题解:
mex这个东西似乎并不能直接用某些数据结构维护;
首先实际上a[i]太大是没有用的,因为如果在首页数字中隔开了一段,那么比那个数大的数不可能对答案有影响;
这样我们就相当于将所有数离散到了200000的级别;
然后利用莫队算法维护当前区间数的集合, 那之后就是查询第一个未覆盖的点了;
一开始脑补的是用一个堆来维护这东西,修改O(logn)查询O(1);
总复杂度是O(m+m√n*logn),用线段树实现了一发TLE了;
我们需要一种能O(1)完成修改的数据结构——分块!
记录一个块内覆盖了多少元素,查询时先找到第一个未完全覆盖的块,再找到块内第一个未覆盖的点;
这样时间复杂度就是O(m√n+m√n)=O(m√n)了;
BZOJ3339双倍经验;
代码:
#include<cctype>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 210000
#define LEN 1<<16
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
using namespace std;
char getc()
{
static char *S,*T,buf[LEN];
if(S==T)
{
T=(S=buf)+fread(buf,1,LEN,stdin);
}
return *S++;
}
int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0';isdigit(ch=getc());)
D=D*10+ch-'0';
return D;
}
struct Query
{
int l,r,pos,no;
friend bool operator <(Query a,Query b)
{
if(a.pos==b.pos)
return a.r<b.r;
return a.pos<b.pos;
}
}Q[N];
int a[N],dis[N];
int ma,bk,l,r;
int c[N],ans[N],cnt[N];
bool cov[N];
void Change(int x,int f)
{
if(a[x]>ma) return ;
if(!c[a[x]])
cov[a[x]]=1,cnt[a[x]/bk]++;
c[a[x]]+=f;
if(!c[a[x]])
cov[a[x]]=0,cnt[a[x]/bk]--;
}
int query()
{
int i;
for(i=0;cnt[i]==bk;i++);
for(i=i*bk;;i++)
if(!cov[i])
return i;
}
int main()
{
int n,m,i,j,k;
n=read(),m=read();
bk=sqrt(n);
for(i=1;i<=n;i++)
dis[i]=a[i]=read();
sort(dis+1,dis+n+1);
for(i=1,ma=-1;i<=n;i++)
{
if(dis[i]==ma+1)
ma++;
else if(dis[i]>ma+1)
break;
}
ma++;
for(i=1;i<=m;i++)
{
Q[i].l=read(),Q[i].r=read();
Q[i].pos=Q[i].l/bk;
Q[i].no=i;
}
sort(Q+1,Q+m+1);
for(i=1,l=1,r=0;i<=m;i++)
{
while(l>Q[i].l) Change(--l,1);
while(r<Q[i].r) Change(++r,1);
while(l<Q[i].l) Change(l++,-1);
while(r>Q[i].r) Change(r--,-1);
ans[Q[i].no]=query();
}
for(i=1;i<=m;i++)
{
printf("%d\n",ans[i]);
}
return 0;
}