bzoj3585 mex
3585: mex
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1445 Solved: 725
[Submit][Status][Discuss]
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
分析:和spoj DQUERY以及bzoj2653类似.都是主席树的妙用.
因为要求mex,每棵主席树内部维护的肯定是权值,维护权值的什么呢? 对于一个区间[l,r], 假设已经固定了右端点r,那么只需要保证左端点即可.当主席树处理到权值区间[x,y]时,如果[x,mid]都在l右边,那么mex肯定只会出现在[mid+1,y]中,否则就会出现在[x,mid]中. 那么第i棵主席树维护的就是下标区间[1,i]中权值的最左位置. 像线段树一样处理.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 200010; int n,m,root[maxn],tot; struct node { int left,right,minn; }e[maxn * 40]; void build(int &o,int l,int r) { o = ++tot; e[o].minn = 0; if (l == r) return; int mid = (l + r) >> 1; build(e[o].left,l,mid); build(e[o].right,mid + 1,r); } void pushup(int o) { int temp = 0x7fffffff; if (e[o].left) temp = min(e[e[o].left].minn,temp); if (e[o].right) temp = min(e[e[o].right].minn,temp); e[o].minn = temp; } void update(int l,int r,int x,int &y,int pos,int v) { e[y = ++tot] = e[x]; if (l == r) { e[y].minn = v; return; } int mid = (l + r) >> 1; if (pos <= mid) update(l,mid,e[x].left,e[y].left,pos,v); else update(mid + 1,r,e[x].right,e[y].right,pos,v); pushup(y); } int query(int l,int r,int x,int y) { if (l == r) return l; int mid = (l + r) >> 1; if (e[e[y].left].minn >= x) return query(mid + 1,r,x,e[y].right); else return query(l,mid,x,e[y].left); } int main() { scanf("%d%d",&n,&m); build(root[0],0,n); for (int i = 1; i <= n; i++) { int x; scanf("%d",&x); if (x >= n) x = n; update(0,n,root[i - 1],root[i],x,i); } for (int i = 1; i <= m; i++) { int l,r; scanf("%d%d",&l,&r); printf("%d\n",query(0,n,l,root[r])); } return 0; }