bzoj 3585 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
Source
题目大意
区间询问mex。
Solution 1 Mo's Algorithm & Block Division
区间求mex?不会,直接莫队。
由于一个数大于等于$n$时无意义,所以按$n$分块,每块记录是否完全被覆盖。
查询时暴力跳。
表示数据真水,最开始某个地方的$x$,写成$p$竟然A了。
Code
1 /** 2 * bzoj 3 * Problem#3585 4 * Accepted 5 * Time: 6832ms 6 * Memory: 5988k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int cs = 500; 13 14 typedef class Query { 15 public: 16 int l, r; 17 int id; 18 19 Query() { } 20 21 boolean operator < (Query b) const { 22 if (l / cs != b.l / cs) return l < b.l; 23 return r < b.r; 24 } 25 }Query; 26 27 int n, m; 28 int* ar; 29 Query* qs; 30 int exist[200005]; 31 int cover[cs]; 32 int *res; 33 34 inline void init() { 35 scanf("%d%d", &n, &m); 36 ar = new int[(n + 1)]; 37 qs = new Query[(m + 1)]; 38 res = new int[(m + 1)]; 39 for (int i = 1; i <= n; i++) 40 scanf("%d", ar + i); 41 for (int i = 1; i <= m; i++) 42 scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].id = i; 43 } 44 45 inline void update(int p, int sign) { 46 int x = ar[p]; 47 if (x >= n) return; 48 if (!exist[x] && sign == 1) cover[x / cs]++; 49 exist[x] += sign; 50 if (!exist[x] && sign == -1)cover[x / cs]--; 51 } 52 53 inline void solve() { 54 sort(qs + 1, qs + m + 1); 55 int mdzzl = 1, mdzzr = 0; 56 for (int i = 1; i <= m; i++) { 57 while (mdzzr < qs[i].r) update(++mdzzr, 1); 58 while (mdzzr > qs[i].r) update(mdzzr--, -1); 59 while (mdzzl < qs[i].l) update(mdzzl++, -1); 60 while (mdzzl > qs[i].l) update(--mdzzl, 1); 61 62 for (int j = 0; j < cs; j++) { 63 if (cover[j] < cs) { 64 int k = j * cs; 65 while (exist[k]) k++; 66 res[qs[i].id] = k; 67 break; 68 } 69 } 70 } 71 72 for (int i = 1; i <= m; i++) 73 printf("%d\n", res[i]); 74 } 75 76 int main() { 77 init(); 78 solve(); 79 return 0; 80 }
Solution 2 Segment Tree
假设你通过某种方式求出了$[1, i]$的答案。
考虑删掉位置1,那么位置上的数到下一次它出现之前都可以用来更新答案。
于是线段树区间修改,单点查询,做完了。
Code
1 /** 2 * bzoj 3 * Problem#3585 4 * Accepted 5 * Time: 4436ms 6 * Memory: 15184k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 #define smin(_a, _b) (_a > _b) ? (_a = _b) : (0) 13 14 typedef class Query { 15 public: 16 int l, r, id, next; 17 }Query; 18 19 typedef class SegTreeNode { 20 public: 21 int val; 22 SegTreeNode *l, *r; 23 24 SegTreeNode():l(NULL), r(NULL) { } 25 }SegTreeNode; 26 27 SegTreeNode pool[500000]; 28 SegTreeNode* top = pool; 29 30 SegTreeNode* newnode(int val) { 31 top->val = val; 32 return top++; 33 } 34 35 typedef class SegTree { 36 public: 37 SegTreeNode* rt; 38 39 SegTree() { } 40 SegTree(int n, int* f) { 41 build(rt, 1, n, f); 42 } 43 44 void build(SegTreeNode*& p, int l, int r, int* f) { 45 p = newnode(200000); 46 if (l == r) { 47 p->val = f[l]; 48 return; 49 } 50 int mid = (l + r) >> 1; 51 build(p->l, l, mid, f); 52 build(p->r, mid + 1, r, f); 53 } 54 55 void update(SegTreeNode* p, int l, int r, int ql, int qr, int val) { 56 if (l == ql && r == qr) { 57 smin(p->val, val); 58 return; 59 } 60 int mid = (l + r) >> 1; 61 if (qr <= mid) 62 update(p->l, l, mid, ql, qr, val); 63 else if (ql > mid) 64 update(p->r, mid + 1, r, ql, qr, val); 65 else { 66 update(p->l, l, mid, ql, mid, val); 67 update(p->r, mid + 1, r, mid + 1, qr, val); 68 } 69 } 70 71 int query(SegTreeNode* p, int l, int r, int idx) { 72 if (l == idx && r == idx) 73 return p->val; 74 int mid = (l + r) >> 1, rt = p->val, a = 211985; 75 if (idx <= mid) 76 a = query(p->l, l, mid, idx); 77 else 78 a = query(p->r, mid + 1, r, idx); 79 return (a < rt) ? (a) : (rt); 80 } 81 }SegTree; 82 83 int n, m; 84 int *ar, *suf; 85 int *last, *res; 86 Query *qs; 87 int *h, *f; 88 SegTree st; 89 boolean *exist; 90 91 inline void init() { 92 scanf("%d%d", &n, &m); 93 h = new int[(n + 1)]; 94 f = new int[(n + 1)]; 95 ar = new int[(n + 1)]; 96 suf = new int[(n + 1)]; 97 res = new int[(m + 1)]; 98 qs = new Query[(m + 1)]; 99 last = new int[(n + 1)]; 100 exist = new boolean[(n + 1)]; 101 fill(h, h + n + 1, 0); 102 fill(suf, suf + n + 1, n + 1); 103 fill(last, last + n + 1, 0); 104 fill(exist, exist + n + 1, false); 105 for (int i = 1, x; i <= n; i++) { 106 scanf("%d", ar + i); 107 if (ar[i] >= n) continue; 108 x = ar[i]; 109 if (last[x]) 110 suf[last[x]] = i; 111 last[x] = i; 112 } 113 for (int i = 1; i <= m; i++) { 114 scanf("%d%d", &qs[i].l, &qs[i].r); 115 qs[i].id = i, qs[i].next = h[qs[i].l], h[qs[i].l] = i; 116 } 117 } 118 119 inline void prepare() { 120 int p = 0; 121 for (int i = 1; i <= n; i++) { 122 if (ar[i] < n) 123 exist[ar[i]] = true; 124 while (exist[p]) p++; 125 f[i] = p; 126 } 127 st = SegTree(n, f); 128 } 129 130 inline void solve() { 131 for (int i = 1; i <= n; i++) { 132 for (int j = h[i]; j; j = qs[j].next) 133 res[qs[j].id] = st.query(st.rt, 1, n, qs[j].r); 134 if (ar[i] < n) { 135 st.update(st.rt, 1, n, i, suf[i] - 1, ar[i]); 136 } 137 } 138 for (int i = 1; i <= m; i++) 139 printf("%d\n", res[i]); 140 } 141 142 int main() { 143 init(); 144 prepare(); 145 solve(); 146 return 0; 147 }