zoukankan      html  css  js  c++  java
  • 带修改离线主席树 + 树状数组 ZOJ

    带修改离线主席树 + 树状数组 ZOJ - 2112【Dynamic Rankings】

    https://cn.vjudge.net/contest/304073#problem/E

    题意

    给定 n 个数和 m 次操作,操作分为两种:

    • Q x y z —— 询问 [x, y] 区间里的第 z 小的数

    • C x y —— 把第 x 个数变成 y

    分析

    mmp突然想起来可以分块...

    这题属于带修改的主席树,需要用到树状数组维护。

    如果用普通静态主席树的做法,每次更新都需要把 “该数所在树和之后的所有树” 都进行更新(由于前缀的性质),这样更新的最坏总复杂度为 (O(n*m)) 显然不行。

    但是可以发现,对于改变 i 位置的值来说,对于 “该数所在树和之后的所有树” 的影响是相同的。因此只要在原来的基础上增加一类树,用于维护更新掉的数,所以我们可以用树状数组来记录更新,每次更新 (log(n)) 棵树。

    因此我们需要用到离线来做,把题目所给定的所有修改后的值也加入 (vector) 中。第一轮建树的时候只需要把修改前的所有值进行建树。

    对于更新,我们不改变这些已经建好的树,而是另建一类树S,用来记录更新。用树状数组来维护,也就是树状数组的每个节点都是一颗线段树。初始时,S[0]、S[1]、S[2]、S[3](树状数组的每个节点)这些都与 root[0] 相同(也就是空树 0)。

    对于 (C 2 6)操作来说,我们只需要减去 2 ,加上 6 即可。对于减去2(树状数组 (i+lowbit(i)) 为 i 的父亲节点, 修改 i,就要把 i 的所有父亲节点都修改了)。因此要更新的是 S[2] 和 S[4] 这两个节点。

    当查询的时候, (sum) 需要另外再加上 S树 的值之差。

    代码

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 5e4+5;
    
    int n, m, cnt;
    int root[maxn];
    int a[maxn];
    vector<int> v;
    int new_n;
    int S[maxn];    // 树状数组维护的新树
    int use[maxn];  // use记录要操作的线段树下标
    
    struct node {
        int l, r, sum;
    }T[maxn*40];
    
    struct Question {
        int f;
        int a, b, c;
    }Q[maxn];
    
    void init() {
        v.clear();
        memset(root, 0, sizeof(root));
        for(int i = 0; i <= n*40; i++) {
            T[i].l = T[i].r = T[i].sum = 0;
        }
        cnt = 0;
    }
    
    int getid(int x) {
        return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
    }
    
    int lowbit(int x) {
        return x & (-x);
    }
    
    void build(int l, int r, int &rt) {
        rt = ++cnt;
        if(l == r) {
            return ;
        }
        int mid = (l+r) / 2;
        build(l, mid, T[rt].l);
        build(mid+1, r, T[rt].r);
    }
    
    void update(int l, int r, int &x, int y, int pos, int f) {
        T[++cnt] = T[y];
        T[cnt].sum += f;    // 1 or -1
        x = cnt;
        if(l == r)
            return ;
        int mid = (l+r) / 2;
        if(mid >= pos) {
            update(l, mid, T[x].l, T[y].l, pos, f);
        }
        else {
            update(mid+1, r, T[x].r, T[y].r, pos, f);
        }
    }
    
    void Add(int x, int c, int f) {
        while(x <= n) {
            update(1, new_n, S[x], S[x], c, f);
            x += lowbit(x);
        }
    }
    
    int Sum(int x) {
        int ans = 0;
        while(x > 0) {
            ans += T[T[use[x]].l].sum;
            x -= lowbit(x);
        }
        return ans;
    }
    
    int query(int l, int r, int a, int b, int x, int y, int k) {
        if(l == r) {
            return l;
        }
        int mid = (l+r) / 2;
        int sum = Sum(b) - Sum(a) + T[T[y].l].sum - T[T[x].l].sum;
        if(sum >= k) {
            for(int i = a; i > 0; i -= lowbit(i)) {
                use[i] = T[use[i]].l;
            }
            for(int i = b; i > 0; i -= lowbit(i)) {
                use[i] = T[use[i]].l;
            }
            return query(l, mid, a, b, T[x].l, T[y].l, k);
        }
        else {
            for(int i = a; i > 0; i -= lowbit(i)) {
                use[i] = T[use[i]].r;
            }
            for(int i = b; i > 0; i -= lowbit(i)) {
                use[i] = T[use[i]].r;
            }
            return query(mid+1, r, a, b, T[x].r, T[y].r, k-sum);
        }
    }
    
    int main() {
        // fopen("in.txt", "r", stdin);
        // fopen("out.txt", "w", stdout);
        int t;
        scanf("%d", &t);
        while(t--) {
            init();
            scanf("%d%d", &n, &m);
            for(int i = 1; i <= n; i++) {
                scanf("%d", &a[i]);
                v.push_back(a[i]);
            }
            for(int i = 1; i <= m; i++) {
                char f[10];
                scanf("%s", f);
                if(f[0] == 'Q') {
                    int l, r, k;
                    scanf("%d%d%d", &l, &r, &k);
                    Q[i].f = 0;
                    Q[i].a = l;
                    Q[i].b = r;
                    Q[i].c = k;
                }
                else {
                    int x, c;
                    scanf("%d%d", &x, &c);
                    Q[i].f = 1;
                    Q[i].a = x;
                    Q[i].b = c;
                    v.push_back(c);
                }
            }
            sort(v.begin(), v.end());
            v.erase(unique(v.begin(), v.end()), v.end());
            new_n = (int)v.size();
            for(int i = 1; i <= n; i++) {
                update(1, new_n, root[i], root[i-1], getid(a[i]), 1);
            }
            for(int i = 1; i <= n; i++) {
                S[i] = root[0];
            }
            for(int i = 1; i <= m; i++) {
                if(Q[i].f == 0) {
                    for(int j = Q[i].a-1; j > 0; j-= lowbit(j)) {
                        use[j] = S[j];
                    }
                    for(int j = Q[i].b; j > 0; j -= lowbit(j)) {
                        use[j] = S[j];
                    }
                    int ans = query(1, new_n, Q[i].a-1, Q[i].b, root[Q[i].a-1], root[Q[i].b], Q[i].c) - 1;
                    printf("%d
    ", v[ans]);
                }
                else {
                    Add(Q[i].a, getid(a[Q[i].a]), -1);  // 减去修改前的值
                    Add(Q[i].a, getid(Q[i].b), 1);   // 加上新值
                    a[Q[i].a] = Q[i].b;
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Linux下编译安装redis
    docker搭建swoole的简易的聊天室
    Linux下面安装swoole
    laravel命令
    史上最污技术解读,60 个 IT 术语我竟然秒懂了.....
    redis集群搭建
    Windows安装MongoDB
    十大经典排序算法(动图演示)
    消息中间件基础
    laravel邮件发送
  • 原文地址:https://www.cnblogs.com/Decray/p/10928452.html
Copyright © 2011-2022 走看看