莫队 mex

来源:互联网 发布:linux查看进程状态 编辑:程序博客网 时间:2024/06/05 19:04

问题 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 201000using 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);}
原创粉丝点击