Luogu 4137 Rmq Problem / mex
一个主席树题。
一开始想着直接动态开点硬搞就可以了,每次查询只要作一个类似于前缀和的东西看看区间有没有满,在主席树上二分就可以了。
但是这样是错的,因为一个权值会出现很多次……然后就错了。
所以我们考虑记录每一个权值最后出现的位置,直接开权值下标记录每一个权值最后出现的位置,因为是区间查询,所以可持久化一下,这样答案就是第一次出现位置小于$l$的最小权值,查询方法类似。
考虑到答案只可能是$a_{i} + 1, 0$,所以直接大力把$a_{i}, a_{i} + 1,0$都丢进去离散化。
注意线段树中权值0出现的位置不是inf,因为0也算自然数。
感觉离线下来也可以不用写可持久化。
自己一开始还是naive
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 4e5 + 5; const int M = 5e6 + 5; const int inf = 1 << 30; int n, qn, tot = 0, a[N], b[N]; inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline int min(int x, int y) { return x > y ? y : x; } namespace PSegT { struct Node { int lc, rc, data; } s[M]; int root[N], nodeCnt; #define mid ((l + r) >> 1) inline void up(int p) { if(p) s[p].data = min(s[s[p].lc].data, s[s[p].rc].data); } void ins(int &p, int l, int r, int x, int pre, int v) { p = ++nodeCnt; s[p].lc = s[pre].lc, s[p].rc = s[pre].rc; if(l == r) { s[p].data = v; return; } if(x <= mid) ins(s[p].lc, l, mid, x, s[pre].lc, v); else ins(s[p].rc, mid + 1, r, x, s[pre].rc, v); up(p); } int query(int p, int l, int r, int x) { if(!p || l == r) return b[l]; if(s[s[p].lc].data < x) return query(s[p].lc, l, mid, x); else return query(s[p].rc, mid + 1, r, x); } } using namespace PSegT; int main() { read(n), read(qn); b[++tot] = 0; for(int i = 1; i <= n; i++) { read(a[i]); b[++tot] = a[i], b[++tot] = a[i] + 1; } sort(b + 1, b + tot + 1); tot = unique(b + 1, b + 1 + tot) - b - 1; root[0] = nodeCnt = 0; //s[0].data = inf; for(int i = 1; i <= n; i++) { a[i] = lower_bound(b + 1, b + 1 + tot, a[i]) - b; ins(root[i], 1, tot, a[i], root[i - 1], i); } for(int x, y; qn--; ) { read(x), read(y); printf("%d\n", query(root[y], 1, tot, x)); } return 0; }