zoukankan      html  css  js  c++  java
  • HDU-4747 Mex 线段树应用 Mex性质

    HDU-4747 Mex 线段树应用 Mex性质

    题意

    给定长度为(n)的数组(a),求

    [sum sum mex(i,j) ]

    其中(mex(i,j))表示区间(mex(a_i...a_j)的值)

    [1leq n leq 2 imes 10^5\ 1leq a_i leq 10^9 ]

    分析

    此题我认为还是不太好想到的

    首先如果只求一维,由于单调性,求(sum mex(1,i))是可以在(O(n))下完成的。

    然后注意到第二维即(sum mex(2,i))该如何计算,这个时候(1)相当于没有了,1在这一维上产生的影响就是当前下一个等于(a[1])的元素之前的一段。后面的显然和原来的保持不变,这就让我们想到了用区间维护。

    那么(a[1])会如何影响([2,next[a[1]] - 1])呢?

    再次想到(mex)在“前缀”意义上的单调性,我们只需要把其中大于(a[1])的部分变为(a[1])即可,其他部分的(mex)并不会受影响

    最后由于递推,不要忘记单点修改。

    所以问题就转化成了

    • 求出每个数的下一个等于它的数出现的位置
    • 求出第一个大于等于(a[i])的位置
    • 修改某一段区间的值

    这些都可以用线段树实现,当然要注意一些细节,比如(lazy)标记应该设置(-1),否则(mx)会无法下传,以及下一个位置数组应该在最后加上(n + 1)

    代码

    struct Tree {
        int lazy;
        int sum;
        int mx;
        int l, r;
    };
    
    int n;
    Tree node[maxn << 2];
    int a[maxn];
    int mex[maxn];
    int nxt[maxn];
    
    void push_up(int i) {
        node[i].sum = node[i << 1].sum + node[i << 1 | 1].sum;
        node[i].mx = max(node[i << 1].mx, node[i << 1 | 1].mx);
    }
    
    void build(int i, int l, int r) {
        node[i].l = l;
        node[i].r = r;
        if (l == r) {
            node[i].sum = mex[l];
            node[i].mx = mex[l];
            return;
        }
        int mid = l + r >> 1;
        build(i << 1, l, mid);
        build(i << 1 | 1, mid + 1, r);
        push_up(i);
    }
    
    void push_down(int i, int m) {
        if (node[i].lazy >= 0) {
            node[i << 1].lazy = node[i].lazy;
            node[i << 1 | 1].lazy = node[i].lazy;
            node[i << 1].sum = node[i].lazy * (m - (m >> 1));
            node[i << 1 | 1].sum = node[i].lazy * (m >> 1);
            node[i << 1].mx = node[i << 1 | 1].mx = node[i].lazy;
            node[i].lazy = -1;
        }
    }
    
    void update(int i, int l, int r, int val) {
        if (node[i].l > r || node[i].r < l) return;
        if (node[i].l >= l && node[i].r <= r) {
            //bug;
            node[i].lazy = val;
            node[i].sum = (node[i].r - node[i].l + 1) * val;
            node[i].mx = val;
            //cout << node[i].mx << ' ' << i << '
    ';
            return;
        }
        push_down(i, node[i].r - node[i].l + 1);
        update(i << 1, l, r, val);
        update(i << 1 | 1, l, r, val);
        push_up(i);
    }
    
    int query(int i, int x) {
        if (node[i].l == node[i].r) {
            return node[i].l;
        }
        if (node[i << 1].mx > x) return query(i << 1, x);
        else return query(i << 1 | 1, x);
    }
    
    
    signed main() {
        while (scanf("%lld", &n)) {
            if (!n) break;
            for (int i = 1; i <= n; i++)
                a[i] = readint(), nxt[i] = n + 1;
            for (int i = 1; i < 4 * n; i++) {
                node[i].l = node[i].r = node[i].sum = node[i].lazy = -1, node[i].mx = 0;
            }
            unordered_map<int, int> mp;
            int cur = 0;
            for (int i = 1; i <= n; i++) {
                if (mp[a[i]]) nxt[mp[a[i]]] = i, mp[a[i]] = i;
                else mp[a[i]] = i;
                while (mp[cur]) cur++;
                mex[i] = cur;
            }
            build(1, 1, n);
            ll ans = 0;
            for (int i = 1; i <= n; i++) {
                ans += node[1].sum;
                if (node[1].mx > a[i]) {
                    int l = query(1,a[i]);
                    int r = nxt[i] - 1;
                    if (l <= r) update(1, l, r, a[i]);
                }
                update(1, i, i, 0);
            }
            cout << ans << '
    ';
        }
    }
    
  • 相关阅读:
    LINUX用户管理——/etc/passwd文件详解
    接口类型的问题:信息丢失
    swift是强类型语言
    swift的多态
    面向对象的本质:基于对象+面向接口+继承
    多态的本质:同一函数接口在接受不同的类型参量时表现出不同的行为--多态是面向接口编程
    类型约束的本质是:类型构造器在约束类型的基础上构造新的函数类型
    类型与函数:函数是一种复合类型,它的构建依赖于数据类型
    类型约束的语义学研究:基于类型约束的编程
    复合类型、类型约束、添加功能、高阶函数
  • 原文地址:https://www.cnblogs.com/hznumqf/p/13875359.html
Copyright © 2011-2022 走看看