標籤:
題目連結:BZOJ - 3585
題目分析
區間mex,即區間中沒有出現的最小自然數。
那麼我們使用一種莫隊+分塊的做法,使用莫隊維護當前區間的每個數位出現次數。
然後求mex用分塊,將權值分塊(顯然mex 一定小於等於 n ,大於 n 的權值沒有意義,可以直接忽略),每塊大小 sqrt(n) 。
然後區間中的某個數的數量被減到0的時候就將它所在的塊的種類計數減一,添加數的時候類似。
然後枚舉每個塊,找到最小的中間有數不存在的塊(即種類數小於塊中的數的種數),然後到這個快裡直接從小一個一個找到第一個不存在的數。
這樣莫隊的複雜度和分塊的複雜度是相加的, O(n^1.5) + O(n^1.5) = O(n^1.5) 。
代碼
#include <iostream>#include <cstdlib>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;const int MaxN = 200000 + 5, MaxQ = 200000 + 5, MaxB = 500 + 5;int n, m, BlkSize, MaxBlk;int A[MaxN], Blk[MaxN], L[MaxB], R[MaxB], Ans[MaxQ], Cnt[MaxN], V[MaxB], Size[MaxB];struct Query{int Idx, l, r, v;} Q[MaxQ];inline bool Cmp(Query q1, Query q2){if (q1.v == q2.v) return q1.r < q2.r;return q1.v < q2.v;}inline void Del_Num(int x){--Cnt[x];if (Cnt[x] == 0) --V[Blk[x]];}inline void Add_Num(int x){if (Cnt[x] == 0) ++V[Blk[x]];++Cnt[x];}void Pull(int f, int x, int y){if (x == y) return;if (f == 0){if (x < y){for (int i = x; i < y; ++i)Del_Num(A[i]);}else{for (int i = x - 1; i >= y; --i)Add_Num(A[i]);}}else{if (x < y) {for (int i = x + 1; i <= y; ++i)Add_Num(A[i]);}else{for (int i = x; i > y; --i)Del_Num(A[i]);}}}int Get_Ans(){int ret = n;for (int i = 1; i <= MaxBlk; ++i)if (V[i] < Size[i]){for (int j = L[i]; j <= R[i]; ++j)if (Cnt[j] == 0){ret = j;break;}break;}return ret;}int main(){scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i){scanf("%d", &A[i]);if (A[i] > n) A[i] = n + 1;}BlkSize = (int)sqrt((double)n);for (int i = 0; i <= n; ++i) Blk[i] = i / BlkSize + 1;MaxBlk = Blk[n];for (int i = 1; i <= MaxBlk; ++i){L[i] = (i - 1) * BlkSize;R[i] = i * BlkSize - 1;Size[i] = R[i] - L[i] + 1;}R[MaxBlk] = n;Size[MaxBlk] = n - L[MaxBlk] + 1;for (int i = 1; i <= m; ++i){scanf("%d%d", &Q[i].l, &Q[i].r);Q[i].Idx = i;Q[i].v = Q[i].l / BlkSize;}sort(Q + 1, Q + m + 1, Cmp);for (int i = Q[1].l; i <= Q[1].r; ++i) Add_Num(A[i]);Ans[Q[1].Idx] = Get_Ans();for (int i = 2; i <= m; ++i){if (Q[i].r <= Q[i - 1].l) {Pull(0, Q[i - 1].l, Q[i].l);Pull(1, Q[i - 1].r, Q[i].r);}else{Pull(1, Q[i - 1].r, Q[i].r);Pull(0, Q[i - 1].l, Q[i].l);}Ans[Q[i].Idx] = Get_Ans();}for (int i = 1; i <= m; ++i) printf("%d\n", Ans[i]);return 0;}
[BZOJ 3585] mex 【莫隊+分塊】