CodeForces Round #678(Div2) E.Complicated Computations Mex性质,权值线段树
CodeForces Round #678(Div2) E.Complicated Computations Mex性质,权值线段树
题意
定义\(mex\)表示给定序列中从1开始第一个没有出现的数
给定一组正整数,问其\(substring\)的\(mex\)的\(mex\)是多少
\[1\leq n \leq 10^5
\\
1\leq 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;
}