您的位置:首页 > 其它

bzoj 3585 mex - 线段树 - 分块 - 莫队算法

2018-02-25 21:10 302 查看

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

By 佚名提供

题目大意

  区间询问mex。

Solution 1 Mo's Algorithm & Block Division

  区间求mex?不会,直接莫队。

  由于一个数大于等于$n$时无意义,所以按$n$分块,每块记录是否完全被覆盖。

  查询时暴力跳。

  表示数据真水,最开始某个地方的$x$,写成$p$竟然A了。

Code

/**
* bzoj
* Problem#3585
* Accepted
* Time: 6832ms
* Memory: 5988k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

const int cs = 500;

typedef class Query {
public:
int l, r;
int id;

Query() {    }

boolean operator < (Query b) const {
if (l / cs  != b.l / cs)    return l < b.l;
return r < b.r;
}
}Query;

int n, m;
int* ar;
Query* qs;
int exist[200005];
int cover[cs];
int *res;

inline void init() {
scanf("%d%d", &n, &m);
ar = new int[(n + 1)];
qs = new Query[(m + 1)];
res = new int[(m + 1)];
for (int i = 1; i <= n; i++)
scanf("%d", ar + i);
for (int i = 1; i <= m; i++)
scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].id = i;
}

inline void update(int p, int sign) {
int x = ar[p];
if (x >= n)    return;
if (!exist[x] && sign == 1)    cover[x / cs]++;
exist[x] += sign;
if (!exist[x] && sign == -1)cover[x / cs]--;
}

inline void solve() {
sort(qs + 1, qs + m + 1);
int mdzzl = 1, mdzzr = 0;
for (int i = 1; i <= m; i++) {
while (mdzzr < qs[i].r)    update(++mdzzr, 1);
while (mdzzr > qs[i].r)    update(mdzzr--, -1);
while (mdzzl < qs[i].l)    update(mdzzl++, -1);
while (mdzzl > qs[i].l)    update(--mdzzl, 1);

for (int j = 0; j < cs; j++) {
if (cover[j] < cs) {
int k = j * cs;
while (exist[k])    k++;
res[qs[i].id] = k;
break;
}
}
}

for (int i = 1; i <= m; i++)
printf("%d\n", res[i]);
}

int main() {
init();
solve();
return 0;
}


分块&莫队

Solution 2 Segment Tree

  假设你通过某种方式求出了$[1, i]$的答案。

  考虑删掉位置1,那么位置上的数到下一次它出现之前都可以用来更新答案。

  于是线段树区间修改,单点查询,做完了。

Code

/**
* bzoj
* Problem#3585
* Accepted
* Time: 4436ms
* Memory: 15184k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define smin(_a, _b) (_a > _b) ? (_a = _b) : (0)

typedef class Query {
public:
int l, r, id, next;
}Query;

typedef class SegTreeNode {
public:
int val;
SegTreeNode *l, *r;

SegTreeNode():l(NULL), r(NULL) {    }
}SegTreeNode;

SegTreeNode pool[500000];
SegTreeNode* top = pool;

SegTreeNode* newnode(int val) {
top->val = val;
return top++;
}

typedef class SegTree {
public:
SegTreeNode* rt;

SegTree() {    }
SegTree(int n, int* f) {
build(rt, 1, n, f);
}

void build(SegTreeNode*& p, int l, int r, int* f) {
p = newnode(200000);
if (l == r) {
p->val = f[l];
return;
}
int mid = (l + r) >> 1;
build(p->l, l, mid, f);
build(p->r, mid + 1, r, f);
}

void update(SegTreeNode* p, int l, int r, int ql, int qr, int val) {
if (l == ql && r == qr) {
smin(p->val, val);
return;
}
int mid = (l + r) >> 1;
if (qr <= mid)
update(p->l, l, mid, ql, qr, val);
else if (ql > mid)
update(p->r, mid + 1, r, ql, qr, val);
else {
update(p->l, l, mid, ql, mid, val);
update(p->r, mid + 1, r, mid + 1, qr, val);
}
}

int query(SegTreeNode* p, int l, int r, int idx) {
if (l == idx && r == idx)
return p->val;
int mid = (l + r) >> 1, rt = p->val, a = 211985;
if (idx <= mid)
a = query(p->l, l, mid, idx);
else
a = query(p->r, mid + 1, r, idx);
return (a < rt) ? (a) : (rt);
}
}SegTree;

int n, m;
int *ar, *suf;
int *last, *res;
Query *qs;
int *h, *f;
SegTree st;
boolean *exist;

inline void init() {
scanf("%d%d", &n, &m);
h = new int[(n + 1)];
f = new int[(n + 1)];
ar = new int[(n + 1)];
suf = new int[(n + 1)];
res = new int[(m + 1)];
qs = new Query[(m + 1)];
last = new int[(n + 1)];
exist = new boolean[(n + 1)];
fill(h, h + n + 1, 0);
fill(suf, suf + n + 1, n + 1);
fill(last, last + n + 1, 0);
fill(exist, exist + n + 1, false);
for (int i = 1, x; i <= n; i++) {
scanf("%d", ar + i);
if (ar[i] >= n)    continue;
x = ar[i];
if (last[x])
suf[last[x]] = i;
last[x] = i;
}
for (int i = 1; i <= m; i++) {
scanf("%d%d", &qs[i].l, &qs[i].r);
qs[i].id = i, qs[i].next = h[qs[i].l], h[qs[i].l] = i;
}
}

inline void prepare() {
int p = 0;
for (int i = 1; i <= n; i++) {
if (ar[i] < n)
exist[ar[i]] = true;
while (exist[p])    p++;
f[i] = p;
}
st = SegTree(n, f);
}

inline void solve() {
for (int i = 1; i <= n; i++) {
for (int j = h[i]; j; j = qs[j].next)
res[qs[j].id] = st.query(st.rt, 1, n, qs[j].r);
if (ar[i] < n) {
st.update(st.rt, 1, n, i, suf[i] - 1, ar[i]);
}
}
for (int i = 1; i <= m; i++)
printf("%d\n", res[i]);
}

int main() {
init();
prepare();
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: