问题 G: mex
时间限制: 2 Sec 内存限制: 128 MB
题目描述
有一个长度为n的数组{a1,a2,…,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
输
第一行n,m。
第二行为n个数。
从第三行开始,每行一个询问l,r。
输出
一行一个数,表示每个询问的答案。
样例输入
5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5
样例输出
1
2
3
0
3
提示
数据规模和约定
对于100%的数据:
1<=n,m<=200000
0<=ai<=109
1<=l<=r<=n
对于30%的数据:
1<=n,m<=1000
第一次做到这种暴力套暴力的题。。莫队套分块。
最开始我想把答案在左右端点推移时就修改,发现太麻烦,反而更慢。
那么以数为块,把所有数加一,就排除了0的情况,最后减回来即可。而且数组不需要开太大,到n即可。因为如果出现>n的数一定不符合。
只是修改分块,记录下这个块有多少个数出现过,最后正大光明地找到第一个不全出现的块,枚举即可。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#define N 201000
using namespace std;
struct Q{int l,r,id;}q[N];
int n,m,h,a[N],sum[N],kuai[N],ans[N];
int b[N],shu[1000];
inline bool cmp(Q a,Q b){return kuai[a.l]==kuai[b.l]?a.r<b.r:kuai[a.l]<kuai[b.l];}
int main()
{
scanf("%d%d",&n,&m);h=sqrt(n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]++,kuai[i]=(i-1)/h+1;
for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+m+1,cmp);
int l=0,r=0;
for(int i=1;i<=m;i++)
{
for(;l<q[i].l;l++)
{
if(a[l]>=n)continue;
sum[a[l]]--;
if(sum[a[l]]==0)shu[kuai[a[l]]]--;
}
for(;l>q[i].l;l--)
{
if(a[l-1]>=n)continue;
if(sum[a[l-1]]==0)shu[kuai[a[l-1]]]++;
sum[a[l-1]]++;
}
for(;r<q[i].r;r++)
{
if(a[r+1]>=n)continue;
if(sum[a[r+1]]==0)shu[kuai[a[r+1]]]++;
sum[a[r+1]]++;
}
for(;r>q[i].r;r--)
{
if(a[r]>=n)continue;
sum[a[r]]--;
if(sum[a[r]]==0)shu[kuai[a[r]]]--;
}
for(int k=1;k<=kuai[n];k++)
if(shu[k]!=h)
{
for(int j=(k-1)*h+1;j<=min(n,k*h);j++)
if(!sum[j]){ans[q[i].id]=j;break;}
break;
}
}
for(int i=1;i<=m;i++)printf("%d
",ans[i]-1);
}