莫队 mex

问题 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\n",ans[i]-1);
}
posted @ 2017-10-06 20:19  Hzoi_QTY  阅读(165)  评论(0编辑  收藏  举报