【bzoj3585/bzoj3339】mex/Rmq Problem 莫队算法+分块
原文地址:http://www.cnblogs.com/GXZlegend/p/6805283.html
题目描述
有一个长度为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
题解
莫队算法+分块,双倍经验题
首先必有如果某数大于等于n,那么它对答案没有任何贡献,所以可以把大于n的数看成n
然后类似于bzoj3809,将权值分块,查询时先找到第一个不满的块,再在块中查找。
注意:自然数:自然数集是全体非负整数组成的集合(包括0),所以分块要从0开始。
#include <cstdio> #include <cmath> #include <algorithm> #define N 200010 using namespace std; struct data { int l , r , bl , id; }a[N]; int w[N] , cnt[N] , num[510] , ans[N]; bool cmp(data a , data b) { return a.bl == b.bl ? a.r < b.r : a.bl < b.bl; } int main() { int n , m , si , i , j , lp = 1 , rp = 0; scanf("%d%d" , &n , &m) , si = (int)sqrt(n); for(i = 1 ; i <= n ; i ++ ) { scanf("%d" , &w[i]); if(w[i] > n) w[i] = n; } for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &a[i].l , &a[i].r) , a[i].bl = (a[i].l - 1) / si , a[i].id = i; sort(a + 1 , a + m + 1 , cmp); n ++ , si = (int)sqrt(n); for(i = 1 ; i <= m ; i ++ ) { while(lp > a[i].l) lp -- , num[w[lp] / si] += (!cnt[w[lp]]) , cnt[w[lp]] ++ ; while(rp < a[i].r) rp ++ , num[w[rp] / si] += (!cnt[w[rp]]) , cnt[w[rp]] ++ ; while(lp < a[i].l) cnt[w[lp]] -- , num[w[lp] / si] -= (!cnt[w[lp]]) , lp ++ ; while(rp > a[i].r) cnt[w[rp]] -- , num[w[rp] / si] -= (!cnt[w[rp]]) , rp -- ; for(j = 0 ; j <= (n - 1) / si ; j ++ ) if(num[j] < si) break; j *= si; while(cnt[j]) j ++ ; ans[a[i].id] = j; } for(i = 1 ; i <= m ; i ++ ) printf("%d\n" , ans[i]); return 0; }