• 自动排行(树状数组套主席树)


    题意

    动态区间第k小。写一个数据结构,支持单点修改,查询区间第k小。

    Solution

    我们回想一下静态第k小是怎么做的,其实就是查询前缀和。

    而这道题如果还按之前的写法,修改的时间复杂度是$O(N)$的,显然不优。

    这时我们就要换一种思路,那就是用树状数组维护前缀和。

    这时树状数组的每个节点维护的不是一个值了,而是一棵主席树(虽然普通的权值线段树也行)。

    对于修改操作,和普通的树状数组一样找到所有要改的节点,让后再它们所代表的主席树上修改,历史版本就是这个节点上一次修改的值。如下:

    void change(int x, int v) {
        int tmp = a[x];
        while (x <= n) {
            ins(rt[x], rt[x], 1, nn, tmp, v);
            x += lowbit(x);
        }
    }

    对于查询,我们首先要预处理出所有要选的主席树(类似树状数组的查询),之后在那些主席树上查询即可。

    查询的思路同静态第k小。

    时间复杂度:$O(N imes log^2{N})$

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 4000010;
    struct Chairman_Tree{
        int ls, rs;
        int val;
    }tr[N * 30];
    int rt[N], tot;
    int n, m;
    int a[N], b[N], nn;
    int L[N], R[N], K[N];
    char opt[N];
    int top1, top2;
    int sk1[N], sk2[N];
    inline int lowbit(int x) {
        return x & -x;
    }
    void ins(int &cur, int pre, int l, int r, int pos, int v) {
        cur = ++tot, tr[cur] = tr[pre], tr[cur].val += v;
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (pos <= mid) ins(tr[cur].ls, tr[pre].ls, l, mid, pos, v);
        else ins(tr[cur].rs, tr[pre].rs, mid + 1, r, pos, v);
    }
    void change(int x, int v) {
        int tmp = a[x];
        while (x <= n) {
            ins(rt[x], rt[x], 1, nn, tmp, v);
            x += lowbit(x);
        }
    }
    void init(int x, int y) {
        top1 = top2 = 0;
        while (x) {
            sk1[++top1] = rt[x];
            x -= lowbit(x);
        }
        while (y) {
            sk2[++top2] = rt[y];
            y -= lowbit(y);
        }
    }
    int query(int l, int r, int k) {
        if (l == r) return l;
        int num = 0;
        for (int i = 1; i <= top1; i++) num -= tr[tr[sk1[i]].ls].val;
        for (int i = 1; i <= top2; i++) num += tr[tr[sk2[i]].ls].val;
        int mid = (l + r) >> 1;
        if (k <= num) {
            for (int i = 1; i <= top1; i++) sk1[i] = tr[sk1[i]].ls;
            for (int i = 1; i <= top2; i++) sk2[i] = tr[sk2[i]].ls;
            return query(l, mid, k);
        } else {
            for (int i = 1; i <= top1; i++) sk1[i] = tr[sk1[i]].rs;
            for (int i = 1; i <= top2; i++) sk2[i] = tr[sk2[i]].rs;
            return query(mid + 1, r, k - num);
        }
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            b[++nn] = a[i];
        }
        for (int i = 1; i <= m; i++) {
            cin >> opt[i];
            scanf("%d%d", &L[i], &R[i]);
            if (opt[i] == 'Q') {
                scanf("%d", &K[i]);
            } else {
                b[++nn] = R[i];
            }
        }
        sort(b + 1, b + nn + 1);
        nn = unique(b + 1, b + nn + 1) - b - 1;
        for (int i = 1; i <= n; i++) {
            int p = lower_bound(b + 1, b + nn + 1, a[i]) - b;
            a[i] = p;
        }
        for (int i = 1; i <= n; i++) {
            change(i, 1);
        }
        for (int i = 1; i <= m; i++) {
            if (opt[i] == 'C') {
                change(L[i], -1);
                a[L[i]] = lower_bound(b + 1, b + nn + 1, R[i]) - b;
                change(L[i], 1);
            } else {
                init(L[i] - 1, R[i]);
                printf("%d
    ", b[query(1, nn, K[i])]);
            }
        }
        return 0;
    }

    显然因为历史版本是自己所以此题维护权值线段树即可。

    可如果要求查看历史版本(比如回到某一次修改前)就必须要用主席树了。

  • 相关阅读:
    css盒子模型
    怎么查看浏览器内核以及浏览器版本
    matlab 读取文件(mat)存储为json文件
    js的闭包
    听别人报告
    关于windows下 python3安装 cython的说明
    python某个module使用了相对引用,同时其__name__又是__main__导致的错误
    python编程指南
    javacc在stanfordnlp中的应用
    hystrix熔断机制修改配置
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/12736888.html
Copyright © 2020-2023  润新知