Loading

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;
}
posted @ 2020-10-26 16:32  MQFLLY  阅读(420)  评论(0编辑  收藏  举报