【BZOJ3585/3339】mex 莫队算法+分块
【BZOJ3585】mex
Description
有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
Input
第一行n,m。
第二行为n个数。
从第三行开始,每行一个询问l,r。
Output
一行一个数,表示每个询问的答案。
Sample Input
5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5
Sample Output
1
2
3
0
3
2
3
0
3
HINT
数据规模和约定
对于100%的数据:
1<=n,m<=200000
0<=ai<=109
1<=l<=r<=n
对于30%的数据:
1<=n,m<=1000
【BZOJ3339】Rmq Problem
题目一样,双倍经验
题解:依然直接上莫队+分块,发现虽然ai≤10^9,但是我们只需要判断它是否≥n,对于≥n的数我们直接领它为n就行了,因为n个自然数最多是0..n-1,出现了≥n的数就一定说明前面有空缺,所以不会对答案产生贡献
于是我们将自然数分块,维护块内不同自然数的个数和区间内自然数的出现次数,就没了~
又体会到了从0开始分块的美好~
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <algorithm> using namespace std; const int maxn=200010; int n,m,siz; struct node { int qa,qb,org; }q[maxn]; int v[maxn],ans[maxn],s[maxn],sk[maxn]; bool cmp(node a,node b) { if((a.qa-1)/siz==(b.qa-1)/siz) return a.qb<b.qb; return (a.qa-1)/siz<(b.qa-1)/siz; } int main() { scanf("%d%d",&n,&m); siz=(int)sqrt((double)n); int i,j; for(i=1;i<=n;i++) scanf("%d",&v[i]),v[i]=(v[i]<=n)?v[i]:n; for(i=1;i<=m;i++) scanf("%d%d",&q[i].qa,&q[i].qb),q[i].org=i; sort(q+1,q+m+1,cmp); int l=1,r=0; for(i=1;i<=m;i++) { while(r<q[i].qb) r++,sk[v[r]/siz]+=(s[v[r]]==0),s[v[r]]++; while(r>q[i].qb) s[v[r]]--,sk[v[r]/siz]-=(s[v[r]]==0),r--; while(l>q[i].qa) l--,sk[v[l]/siz]+=(s[v[l]]==0),s[v[l]]++; while(l<q[i].qa) s[v[l]]--,sk[v[l]/siz]-=(s[v[l]]==0),l++; for(j=0;j*siz+siz-1<=n&&sk[j]==siz;j++); for(j=j*siz;s[j]>0;j++); ans[q[i].org]=j; } for(i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
| 欢迎来原网站坐坐! >原文链接<