[BZOJ3585]mex(莫队+分块)

显然可以离线主席树,这里用莫队+分块做。分块的一个重要思想是实现修改与查询时间复杂度的均衡,这里莫队和分块互相弥补。

考虑暴力的分块做法,首先显然大于n的数直接忽略,于是将值域分成sqrt(n)份,每块记录块内的所有值是否在此当前区间内都已存在。

这样每次暴力从L到R分别放入这个表,最后从小到大询问每个块是否已满,若没有则在块内枚举第一个不存在的数。

注意到这样的总修改复杂度O(nq),查询复杂度O(qsqrt(n))。

考虑莫队,将序列分成sqrt(n)份,使总修改复杂度变为O(nsqrt(n))。查询复杂度不变O(qsqrt(n))。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 using namespace std;
 5 
 6 const int N=200010,K=810;
 7 int n,m,B,a[N],b[N],ans[N],cnt[K],s[K][K];
 8 struct P{ int l,r,id; }q[N];
 9 
10 bool cmp(const P &x,const P &y){ return b[x.l]!=b[y.l] ? b[x.l]<b[y.l] : x.r<y.r; }
11 
12 void add(int x){
13     if (x>n) return;
14     int t=x%B; s[b[x]][t]++; if (s[b[x]][t]==1) cnt[b[x]]++;
15 }
16 
17 void del(int x){
18     if (x>n) return;
19     int t=x%B; s[b[x]][t]--; if (s[b[x]][t]==0) cnt[b[x]]--;
20 }
21 
22 int Que(){
23     rep(i,1,n/B+1){
24         int t=min(n,i*B-1)-(i-1)*B+1;
25         if (cnt[i]==t) continue;
26         rep(j,(i-1)*B,min(n,i*B-1)) if (!s[i][j%B]) return j;
27     }
28     return n+1;
29 }
30 
31 int main(){
32     freopen("bzoj3585.in","r",stdin);
33     freopen("bzoj3585.out","w",stdout);
34     scanf("%d%d",&n,&m); B=800; b[0]=1;
35     rep(i,1,n) scanf("%d",&a[i]),b[i]=i/B+1;
36     rep(i,1,m) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
37     sort(q+1,q+m+1,cmp); int L=1,R=0;
38     rep(i,1,m){
39         while (R<q[i].r) R++,add(a[R]);
40         while (L>q[i].l) L--,add(a[L]);
41         while (R>q[i].r) del(a[R]),R--;
42         while (L<q[i].l) del(a[L]),L++;
43         ans[q[i].id]=Que();
44     }
45     rep(i,1,m) printf("%d
",ans[i]);
46     return 0;
47 }
原文地址:https://www.cnblogs.com/HocRiser/p/10397076.html