题目描述
有一个长度为n的数组{a1,a2,…,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
输入输出格式
输入格式:第一行n,m。
第二行为n个数。
从第三行开始,每行一个询问l,r。
输出格式:一行一个数,表示每个询问的答案。
输入输出样例
输入样例#1:
5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5
输出样例#1:
1
2
3
0
3
说明
对于30%的数据:1<=n,m<=1000
对于100%的数据:1<=n,m<=200000,0<=ai<=10^9,1<=l<=r<=n
Solution:
本题又是区间上的操作,而且操作还这么诡异,于是果断往莫队上想。
很容易想到维护一段区间$[l,r]$的最小值$ansmin$,用一个数组统计每个数字出现的次数,然后每次对$ansmin$的值进行修改,具体就是当某数字增加时暴力求出当前最小的没有出现的数,当某数字减少时若减小到$0$则与$ansmin$取最小值就行了。
但是有个很大的问题,本题的数据$a[i] leq 10^9$,这就是真的骚,果断切掉这算法,看波题解,发现别人竟然用这方法$AC$了,真的神奇。于是忽略数据范围来乱写,果真$AC$了。
最蛇皮的是,我想试一下模数取$520$能不能过,结果$10$分,神奇的是我换回原来代码后,死活$AC$不了,什么$RE,TLE,WA$都出来了,简直神奇。
后面想到一个问题就是答案肯定最大为$200001$以内,因为一共就$200000$个数,每个被覆盖,最大显然是$200001$。
于是就可以直接将比$200000$大的数去掉就OK了。整个复杂度是玄学的,嘿嘿!(巨说正解的mex求法就是分块,可以去学学)
看了一下别人的算法,都是什么高超的数据结构(挖个坑下次学学用权值线段树+离线或者持久化写写这题),但还是贴一下莫队的代码吧,本题真蛇皮啊~~手动滑稽~~。
代码:
#include<bits/stdc++.h> #define il inline #define ll long long using namespace std; const int N=200005; int n,m,ansmin,pos[N],a[N],ans[N],s[N]; struct data{ int l,r,id; }t[N]; il int gi() { int a=0;char x=getchar();bool f=0; while((x<'0'||x>'9')&&x!='-')x=getchar(); if(x=='-')x=getchar(),f=1; while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); return f?-a:a; } il bool cmp(data a,data b){return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;} il void change(int k,int u) { if(a[k]>200000)return; if(u==1){ s[a[k]]++;int i=ansmin;while(s[i])i++;ansmin=i; } else { s[a[k]]--;if(!s[a[k]])ansmin=min(ansmin,a[k]); } } int main() { n=gi(),m=gi(); int ss=int(sqrt(n)); for(int i=1;i<=n;i++)a[i]=gi(),pos[i]=(i-1)/ss+1; for(int i=1;i<=m;i++)t[i].l=gi(),t[i].r=gi(),t[i].id=i; sort(t+1,t+m+1,cmp); for(int i=1,l=1,r=0;i<=m;i++){ while(r<t[i].r)change(r+1,1),r++; while(r>t[i].r)change(r,-1),r--; while(l<t[i].l)change(l,-1),l++; while(l>t[i].l)change(l-1,1),l--; if(t[i].l==t[i].r){ans[t[i].id]=0;continue;} ans[t[i].id]=ansmin; } for(int i=1;i<=m;i++)printf("%d ",ans[i]); return 0; }