• @bzoj



    @description@

    Input

    Output

    Sample Input
    7 5
    0 2 1 0 1 3 2
    1 3
    2 3
    1 4
    3 6
    2 7
    Sample Output
    3
    0
    3
    2
    4

    Hint

    @solution@

    在线不大好搞,因为 mex 函数不大支持合并,也没有比较好用的性质。
    离线,如果采用每次加入一个数的方法也不大好搞。

    但是反过来,假如我现在的区间的 mex 是 ans,删掉一个 a[x] 后如果剩下的区间没有与 a[x] 相同的数,可以得到新的 ans' = min(ans, a[x])。
    这个证明还是比较直观的。也就是说我们可以写莫队了。我们接下来的算法就利用这个性质。

    考虑固定左端点 l,维护以 l~n 中的下标作为右端点的 mex 值。记录 nxt[l] 表示 l 之后最近的与 a[l] 相同的位置。
    每次从 l 转移到 l + 1 时就直接对区间 [l, nxt[l] - 1] 进行区间对 a[l] 取 min 的操作即可。
    因为 mex 值是单调的(越远越大),这个很容易就线段树维护出来了。查询直接离线在线段树上查就可以了。
    也许可以强制在线然后用可持久化线段树搞。

    @accepted code@

    #include<cstdio>
    #include<vector>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int MAXN = 200000;
    vector<pair<int, int> >vec[MAXN + 5];
    int n, m, a[MAXN + 5], ans[MAXN + 5];
    int mex[MAXN + 5];
    struct segtree{
        struct node{
            int l, r, mtag;
        }t[4*MAXN + 5];
        void build(int x, int l, int r) {
            t[x].l = l, t[x].r = r, t[x].mtag = MAXN;
            if( l == r ) return ;
            int mid = (l + r) >> 1;
            build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
        }
        void modify(int x, int l, int r, int k) {
            if( l > t[x].r || r < t[x].l )
                return ;
            if( l <= t[x].l && t[x].r <= r ) {
                t[x].mtag = min(t[x].mtag, k);
                return ;
            }
            modify(x << 1, l, r, k), modify(x << 1 | 1, l, r, k);
        }
        int query(int x, int p) {
            if( t[x].l == t[x].r ) return min(t[x].mtag, mex[p]);
            int mid = (t[x].l + t[x].r) >> 1;
            if( p <= mid ) return min(t[x].mtag, query(x << 1, p));
            else return min(t[x].mtag, query(x << 1 | 1, p));
        }
    }T;
    bool tag[MAXN + 5];
    int nxt[MAXN + 5], adj[MAXN + 5];
    void solve() {
        int pos = 0;
        for(int i=1;i<=n;i++) {
            tag[a[i]] = true;
            while( tag[pos] ) pos++;
            mex[i] = pos;
        }
        for(int i=n;i>=1;i--) {
            nxt[i] = (adj[a[i]] ? adj[a[i]] : n + 1);
            adj[a[i]] = i;
        }
        T.build(1, 1, n);
        for(int i=1;i<=n;i++) {
            for(int j=0;j<vec[i].size();j++)
                ans[vec[i][j].second] = T.query(1, vec[i][j].first);
            T.modify(1, 1, nxt[i]-1, a[i]);
        }
    }
    int main() {
        scanf("%d%d", &n, &m);
        for(int i=1;i<=n;i++)
            scanf("%d", &a[i]);
        for(int i=1;i<=m;i++) {
            int L, R; scanf("%d%d", &L, &R);
            vec[L].push_back(make_pair(R, i));
        }
        solve();
        for(int i=1;i<=m;i++)
            printf("%d
    ", ans[i]);
    }
    

    @details@

    不得不说,通过删除一个数来维护新的区间信息的题目还是比较少见的。因为大多数题目都是维护加入一个数的情况。

    人类智慧。

  • 相关阅读:
    ViewPagerAdapter
    Android Touch事件传递机制详解
    android ANR产生原因和解决办法【转】
    Android 操作系统的内存回收机制(转载)
    android的程序运行数据存放在哪里?
    自定义RecyclerView.ItemDecoration,实现RecyclerView的分割线效果
    Android中 Bitmap和Drawable相互转换的方法
    android 存储图片到data目录和读取data目录下的图片
    多线程调用HttpWebRequest并发连接限制
    反射
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11304182.html
Copyright © 2020-2023  润新知