CodeForces Round #678(Div2) E.Complicated Computations Mex性质,权值线段树
题意
定义(mex)表示给定序列中从1开始第一个没有出现的数
给定一组正整数,问其(substring)的(mex)的(mex)是多少
[1leq n leq 10^5
\
1leq a_i leq n
]
分析
此题和(HDU-6767)有点相似,都用到了线段树和mex的性质,只不过那题似乎比这题麻烦一点
我们考虑先求出所有(substring)的(mex),如果一个区间的(mex = a),那么容易发现只需满足如下条件
- 区间中没有出现(a)
- 区间中出现了(1 到 a - 1)
想象在考虑(a)的时候把整个数组分割成了若干个不含(a)的子段,那么对于其中一个(a),它只会影响到上一个或者下一个(a)之后的区间,
这样就只需要满足第二个条件,而这个可以用权值线段树维护
线段树维护(a_i)最后一次的位置,动态维护一颗权值线段树即可
操作只需要单点修改,区间查询
注意用(last)维护的是上一个位置,因此最后还需扫一遍,来确定后面的贡献
代码
int last[maxn], a[maxn];
int val[maxn << 2];
int vis[maxn];
void update(int i, int l, int r, int pos, int v) {
if (l == r) {
val[i] = v;
return;
}
int mid = l + r >> 1;
if (pos <= mid) update(i << 1, l, mid, pos, v);
else update(i << 1 | 1, mid + 1, r, pos, v);
val[i] = min(val[i << 1], val[i << 1 | 1]);
}
int query(int i, int l, int r, int L, int R) {
if (l == L && r == R) {
return val[i];
}
int mid = l + r >> 1;
if (R <= mid) return query(i << 1, l, mid, L, R);
else if (L > mid) return query(i << 1 | 1, mid + 1, r, L, R);
else return min(query(i << 1, l, mid, L, mid), query(i << 1 | 1, mid + 1, r, mid + 1, R));
}
int main() {
int n = readint();
for (int i = 1; i <= n; i++)
a[i] = readint();
for (int i = 1; i <= n; i++) {
if (a[i] != 1) vis[1] = 1;
if (a[i] > 1 && query(1, 1, n, 1,a[i] - 1) > last[a[i]]) vis[a[i]] = 1;
last[a[i]] = i;
update(1, 1, n, a[i], i);
}
for (int i = 2; i <= n + 1; i++)
if (query(1, 1, n, 1, i - 1) > last[i]) vis[i] = 1;
int res = 1;
while (vis[res]) res++;
cout << res;
}