维护后面的position sg函数概念,离线+线段 bzoj 3339
3339: Rmq Problem
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1160 Solved: 596
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
7 5
0 2 1 0 1 3 2
1 3
2 3
1 4
3 6
2 7
0 2 1 0 1 3 2
1 3
2 3
1 4
3 6
2 7
Sample Output
3
0
3
2
4
0
3
2
4
HINT
Source
http://www.lydsy.com/JudgeOnline/problem.php?id=3339
思路:
首先,我们预处理处[1,i]区间中的mex函数,即mex[i]为(1~i)中没有出现过的数字。
然后对于区间的移动,从[l,r]->[l+1,r],我们定义next[l]表示下一次a[l]出现的位置。然后我们发现,如果next[l] >= r,那么区间[l,r]和[l+1,r]的sg函数是不一样的,所以,我们对于[ l+1,next[l]-1 ]区间进行修改操作,对mex取min的就好了。然后这步操作是区间操作,我们用线段树来解决就行。
然后我们开始从左到右暴力一遍,并且通过线段树来维护,lazy一下即可。
这题最关键的部分就是在于询问部分!
因为询问的话是询问一个区间[l,r]的,但是我们只需要询问第r个位置的mex的值是多少就好了。(因为线段树更新以后的[l+1, next[l] - 1 ]会对区间最小造成干扰,所以我们只需要知道在L之前,有没有一个区间能更新到R即可,所以就只需要查询R这个点)
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #pragma comment(linker,"/STACK:102400000,102400000") #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second #define haha printf("haha\n") const int maxn = 200000 + 5; vector<pair<int, int> > ve[maxn]; int tree[maxn << 2], lazy[maxn << 2]; int n, q; int a[maxn], mex[maxn]; bool vis[maxn]; int nxt[maxn], pos[maxn]; void build_tree(int l, int r, int o){ lazy[o] = -1; if (l == r){ tree[o] = mex[l]; return ; } int mid = (l + r) / 2; build_tree(l, mid, o << 1); build_tree(mid + 1, r, o << 1 | 1); tree[o] = min(tree[o << 1], tree[o << 1 | 1]); } void push_down(int o){ int lb = o << 1, rb = o << 1 | 1; if (lazy[lb] == -1 || lazy[lb] > lazy[o]){ lazy[lb] = lazy[o]; tree[lb] = min(tree[lb], lazy[lb]); } if (lazy[rb] == -1 || lazy[rb] > lazy[o]){ lazy[rb] = lazy[o]; tree[rb] = min(tree[rb], lazy[rb]); } tree[o] = -1; } int query(int x, int l, int r, int o){ if (x == l && x == r){ return tree[o]; } if (lazy[o] != -1) push_down(o); int mid = (l + r) / 2; if (x <= mid) return query(x, l, mid, o << 1); if (x > mid) return query(x, mid + 1, r, o << 1 | 1); } void update(int ql, int qr, int l, int r, int o, int val){ if (ql <= l && qr >= r){ if (lazy[o] == -1) lazy[o] = val; lazy[o] = min(lazy[o], val); tree[o] = min(lazy[o], tree[o]); return ; } if (lazy[o] != -1)push_down(o); int mid = (l + r) / 2; if (ql <= mid) update(ql, qr, l, mid, o << 1, val); if (qr > mid) update(ql, qr, mid + 1, r, o << 1 | 1, val); tree[o] = min(tree[o << 1], tree[o << 1 | 1]); } int ans[maxn]; void solve(){ build_tree(1, n, 1); for (int i = 1; i <= n; i++){ for (int j = 0; j < ve[i].size(); j++){ int pos = ve[i][j].fi, id = ve[i][j].se; ans[id] = query(pos, 1, n, 1); } int lb = i + 1, rb = nxt[i] - 1; if (lb <= rb) update(lb, rb, 1, n, 1, a[i]); } for (int i = 1; i <= q; i++){ printf("%d\n", ans[i]); } } int main(){ cin >> n >> q; for (int i = 1; i <= n; i++) { scanf("%d", a + i); vis[a[i]] = true; mex[i] = mex[i - 1]; while (vis[mex[i]]) mex[i]++; pos[i] = n + 1; } for (int i = 0; i <= n; i++) pos[i] = n + 1; for (int i = n; i >= 1; i--){ nxt[i] = pos[a[i]]; pos[a[i]] = i; } for (int i = 1; i <= q; i++){ int l, r; scanf("%d%d", &l, &r); ve[l].pb(mk(r, i)); } solve(); return 0; }