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

Sample Output

1
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 }

转载于:https://www.cnblogs.com/yyf0309/p/8470652.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值