BZOJ3585: mex
【传送门:BZOJ3585】
简要题意:
给出一个长度为n的数列,有m个询问,每个询问输入l,r,求出l到r之间没出现过的最小自然数
题解:
莫队+权值分块
只要处理每一个权值块总共出现多少种数,每当找到一个权值块出现的种数不等于总共的种数,就直接在这个块里找哪个没出现过就行了
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; struct question { int l,r,id,d; }q[210000]; int belong[210000],L[1100],R[1100],block; bool cmp1(question n1,question n2) { if(belong[n1.l]<belong[n2.l]) return true; if(belong[n1.l]>belong[n2.l]) return false; if(n1.r<n2.r) return true; if(n1.r>n2.r) return false; return false; } bool cmp2(question n1,question n2) { return n1.id<n2.id; } int a[210000],d[210000]; int sum[1100]; void del(int x) { d[x]--; if(d[x]==0) sum[belong[x]]--; } void add(int x) { d[x]++; if(d[x]==1) sum[belong[x]]++; } int solve() { for(int i=1;i<=block;i++) { if(sum[i]!=R[i]-L[i]+1) { for(int j=L[i];j<=R[i];j++) { if(d[j]==0) return j; } } } } int main() { int n,m; scanf("%d%d",&n,&m); block=int(sqrt(n)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]);a[i]++; if(a[i]>n) a[i]=n; int t=(i-1)/block+1; belong[i]=t; if(L[t]==0) R[t-1]=i-1,L[t]=i; } R[belong[n]]=n; 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,cmp1); int l=1,r=0; memset(d,0,sizeof(d)); for(int i=1;i<=m;i++) { while(l<q[i].l) del(a[l]),l++; while(l>q[i].l) l--,add(a[l]); while(r<q[i].r) r++,add(a[r]); while(r>q[i].r) del(a[r]),r--; q[i].d=solve()-1; } sort(q+1,q+m+1,cmp2); for(int i=1;i<=m;i++) printf("%d\n",q[i].d); return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚