• 关于区间 $mex$ 的几种做法


    关于区间 (mex) 的几种做法

    题目链接

    题目大意

    在求 (SG) 函数时提到过一个 (mex) 函数;
    (mex({a_i})) 表示在 (a) 中未出现的最小自然数,其中 (a_i in N)

    给一个长度为 (n) 的序列 (a)(m) 次查询,每次查询 (mex({a_i}),i in [l,r])

    1、莫队+树状数组

    权值树状数组维护每个数出现次数;
    复杂度:(O(m sqrt n logn))

    2、莫队+分块

    把权值分块,每个块维护该块有几个数出现过;
    复杂度:(O((n + m) sqrt n));
    代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    
    const int N = 2e5 + 5;
    
    int blo, bl[N];
    
    struct query {
        int l, r, id;
    } q[N];
    int n, m;
    int a[N], res[N];
    
    inline bool cmp (const query &i, const query &j) {
        if (bl[i.l] != bl[j.l])
            return bl[i.l] < bl[j.l];
        if (bl[i.l] & 1)
            return i.r < j.r;
        return i.r > j.r;
    }
    
    struct block_split {
        int a[N], b[1000];
        inline void modify(int p, int k) {
            if (!a[p])
                ++b[bl[p]];
            a[p] += k;
            if (!a[p])
                --b[bl[p]];
        }
        int query() {
            int i;
            for (i = 1; i < bl[n]; ++i)
                if (b[i] < blo)
                    break;
            for (int j = (i - 1) * blo; j <= std::min(n, i * blo - 1); ++j)
                if (!a[j])
                    return j;
            return n;
        }
    } B;
    
    inline void add(const int p) {
        B.modify(a[p], 1);
    }
    
    inline void rem(const int p) {
        B.modify(a[p], -1);
    }
    
    int main() {
        n = in(), m = in();
        for (int i = 1; i <= n; ++i)
            a[i] = in(), chk_min(a[i], n);
        for (int i = 1; i <= m; ++i)
            q[i] = (query){in(), in(), i};
        blo = (int)sqrt(n + 1);
        for (int i = 0; i <= n; ++i)
            bl[i] = i / blo + 1;
        std::sort(q + 1, q + 1 + m, cmp);
        for (int i = 1, l = 1, r = 0; i <= m; ++i) {
            for (; l > q[i].l; add(--l));
            for (; r < q[i].r; add(++r));
            for (; l < q[i].l; rem(l++));
            for (; r > q[i].r; rem(r--));
            res[q[i].id] = B.query();
        }
        for (int i = 1; i <= m; ++i)
            printf("%d
    ", res[i]);
        return 0;
    }
    

    3、主席树

    上述做法要离线,主席树可以处理在线询问,且复杂度较优秀。
    主席树维护每个数在第 (i) 个历史版本前,最后一次出现的位置。
    对于一组查询 (l, r),在第 (r) 个历史版本中找到 最后一次出现在第 (l) 个历史版本前 最小的数。
    代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    int in() {
        int x = 0; char c = getchar();
        while (c < '0' || c > '9')
            c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return x;
    }
    
    const int N = 2e5 + 5;
    
    struct persistable_tree {
        int min[20 * N], rt[20 * N], c[20 * N][2];
        int tot;
    
        void modify(int pos, int k, int tl, int tr, int pre, int &p) {
            p = ++tot;
            if (tl == tr)
                return (void)(min[p] = k);
            c[p][0] = c[pre][0], c[p][1] = c[pre][1];
            int mid = (tl + tr) >> 1;
            if (mid >= pos)
                modify(pos, k, tl, mid, c[pre][0], c[p][0]);
            else
                modify(pos, k, mid + 1, tr, c[pre][1], c[p][1]);
            min[p] = std::min(min[c[p][0]], min[c[p][1]]);
        }
    
        int query(int k, int tl, int tr, int p) {
            if (tl == tr)
                return tl;
            int mid = (tl + tr) >> 1;
            if (min[c[p][0]] < k)
                return query(k, tl, mid, c[p][0]);
            else
                return query(k, mid + 1, tr, c[p][1]);
        }
    } T;
    
    int main() {
        int n = in(), m = in();
        for (int i = 1, x; i <= n; ++i) {
            x = in();
            T.modify(x, i, 0, n, T.rt[i - 1], T.rt[i]);
        }
        int l, r;
        while (m--) {
            l = in(), r = in();
            printf("%d
    ", T.query(l, 0, n, T.rt[r]));
        }
        return 0;
    }
    
  • 相关阅读:
    Appium + Python自动化
    Appium + python自动化
    基于python的request库,模拟登录csdn博客
    利用uiautomator实现Android移动app启动时间的测试
    JavaScript 闭包
    Vue.js 参考资料汇总
    JVM垃圾回收策略
    JVM内存分配策略
    synchronized底层原理
    HTTPS加密原理
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/10983951.html
Copyright © 2020-2023  润新知