CodeForces Round #678(Div2) E.Complicated Computations Mex性质,权值线段树

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;
}
原文地址:https://www.cnblogs.com/hznumqf/p/13879185.html