zoukankan      html  css  js  c++  java
  • 主席树动态更新

    静态主席树就是求某个区间第k大,而动态主席树就需要一边查询区间第k大,还要一边进行单点更新。

    学习动态主席树需要了解的知识:静态主席树,数组数组,离线操作。

    动态主席树的结构:树状数组套主席树 + 静态主席树。

    题目:洛谷P2617

    https://www.luogu.org/problem/P2617

    代码:

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 5e5+7;
    
    vector<int> v;  //存储离散化后的数组
    
    struct Query {
        int l, r, k;
        bool op;
    }Q[maxn];   //提前存储需要用到的指令,便于之后的离散化
    
    int root[maxn * 40];    //存储静态主席树的根节点
    int T[maxn * 40];       //存储动态主席树的根节点
    int arr[maxn];          //存储输入的N个值
    int lson[maxn * 40], rson[maxn * 40];   //存储左右子树的结点
    int ls[maxn * 40], rs[maxn * 40];   //存储更新时用到的值
    int sum[maxn * 40];     //存储某个区间的值的个数
    int cnt;    
    int size;   //区间的长度大小
    int n, m;
    
    int lowbit(int x) {
        return x & (-x);
    }
    
    int find(int x) {
        return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
    }
    
    void build(int l, int r, int &cur) {
        cur = ++cnt;
        sum[cur] = 0;
        if(l == r) {
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, lson[cur]);
        build(mid + 1, r, rson[cur]);
    }
    
    void update(int l, int r, int pre, int &cur, int pos, int val) {
        cur = ++cnt;
        lson[cur] = lson[pre];
        rson[cur] = rson[pre];
        sum[cur] = sum[pre] + val;
        if(l == r) {
            return;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid) {
            update(l, mid, lson[pre], lson[cur], pos, val);
        } else {
            update(mid + 1, r, rson[pre], rson[cur], pos, val);
        }
    }
    
    void change(int x, int pos, int val) {
        while(x <= n) {
            update(1, size, T[x], T[x], pos, val);
            x += lowbit(x);
        }
    }
    
    int query(int l, int r, int pre, int cur, int k) {
        if(l == r) {
            return l;
        }
        int mid = (l + r) >> 1;
        int res = sum[lson[cur]] - sum[lson[pre]];  //此处时静态主席树中所求区间[left, right]中的值
        //下面两个for循环时求动态主席树中所求区间[left, right]中的值
        for(int i = 1; i <= ls[0]; i++) {
            res -= sum[lson[ls[i]]];
        }
        for(int i = 1; i <= rs[0]; i++) {
            res += sum[lson[rs[i]]];
        }
    
        //解释:静态主席树中的值是原来N个数的值,而动态主席树中是之后更新过后的值,需要将两个相加起来才能求得总值
    
        if(res >= k) {
            //下面两个for循环是将每个结点得左子树更新到数组中去
            for(int i = 1; i <= ls[0]; i++) {
                ls[i] = lson[ls[i]];
            }
            for(int i = 1; i <= rs[0]; i++) {
                rs[i] = lson[rs[i]];
            }
            return query(l, mid, lson[pre], lson[cur], k);
        } else {
            //同上,更新右子树
            for(int i = 1; i <= ls[0]; i++) {
                ls[i] = rson[ls[i]];
            }
            for(int i = 1; i <= rs[0]; i++) {
                rs[i] = rson[rs[i]];
            }
            return query(mid + 1, r, rson[pre], rson[cur], k - res);
        }
    }
    
    int main() {
        scanf("%d %d", &n, &m);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &arr[i]);
            v.push_back(arr[i]);
        }
        for(int i = 0; i < m; i++) {
            char str[5];
            scanf("%s", str);
            if(str[0] == 'Q') {
                scanf("%d %d %d", &Q[i].l, &Q[i].r, &Q[i].k);
                Q[i].op = true;
            } else {
                scanf("%d %d", &Q[i].l, &Q[i].r);
                Q[i].op = false;
                v.push_back(Q[i].r);
            }
        }
    
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());   //基本离散化操作
    
        size = v.size();    //获取区间长度
    
        build(1, size, root[0]);
        for(int i = 1; i <= n; i++) {       //创建静态主席树
            update(1, size, root[i - 1], root[i], find(arr[i]), 1);
        }
    
        for(int i = 1; i <= n; i++) {       //初始化动态主席树
            T[i] = root[0];
        }
        for(int i = 0; i < m; i++) {
            if(Q[i].op) {
                //下面两个for循环是在树状数组中获取在所求区间中得有关根节点
                ls[0] = rs[0] = 0;
                for(int j = Q[i].l - 1; j > 0; j -= lowbit(j)) {
                    ls[++ls[0]] = T[j];
                }
                for(int j = Q[i].r; j > 0; j -= lowbit(j)) {
                    rs[++rs[0]] = T[j];
                }
    
                int pos = query(1, size, root[Q[i].l - 1], root[Q[i].r], Q[i].k);
                printf("%d
    ", v[pos - 1]);
            } else {
                //第一次是将原有得值删掉,第二次是添加新得值
                change(Q[i].l, find(arr[Q[i].l]), -1);
                arr[Q[i].l] = Q[i].r;   //更新原数组中得值
                change(Q[i].l, find(Q[i].r), 1);
            }
        }
        return 0;
    }
  • 相关阅读:
    Linux 打包文件 及 备份数据库
    YII事务
    MySQL两种存储引擎: MyISAM和InnoDB 简单总结
    mysql锁表查询和解锁操作
    Yii+MYSQL锁表防止并发情况下重复数据的方法
    B/S和C/S的区别及应用【转】
    Yii2.0的乐观锁与悲观锁
    【事务】脏读、不可重复读、幻读解释
    利用非阻塞的文件排他锁
    自定义实例化class
  • 原文地址:https://www.cnblogs.com/buhuiflydepig/p/11261001.html
Copyright © 2011-2022 走看看