zoukankan      html  css  js  c++  java
  • 『学习笔记』可持久化线段树(主席树)

    主席树的最基础的操作就是查询历史版本区间第 \(k\) 大,带修。

    这个问题的基础解决思路:对于每次修改都建一棵权值线段树,显然空间开不下。

    这时可持久化线段树的思路就应运而生了。

    主要思想:

    不难发现,每次修改只会有一条链上的值发生改变,所以我们不需要建出整棵新树,只需要把新建那条链上的点即可。

    所以空间就是 \(n\log n\) 的。

    我们还要在权值线段树上做个前缀和来方便查询,每次查询时,就是在权值线段树上二分的过程。

    注意到我们要建的是权值线段树,所以要对输入的值做一个离散化。

    不多说了,直接贴代码吧。

    \(code:\)

    #include <bits/stdc++.h>
    #define uint unsigned int
    #define ls(x) t[x].l
    #define rs(x) t[x].r
    
    using namespace std;
    
    namespace IO{
        inline int read(){
            int x = 0, f = 1;
            char ch = getchar();
            while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
            while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
            return x * f;
        }
    
        inline char readc(){
            char ch = getchar();
            while(ch < 'a' || ch > 'z') ch = getchar();
            return ch;
        }
    
        template <typename T> inline void write(T x){
            if(x > 9) write(x / 10);
            putchar(x % 10 + '0');
        }
    }
    using namespace IO;
    
    const int N = 2e5 + 10;
    int n, m, cnt;
    int a[N], b[N];
    struct Tree{
        int rt, sum, l, r;
        Tree() {sum = 0;}
    }t[N << 5];
    
    inline int build(int l, int r){
        int rt = ++cnt;
        if(l == r) return rt;
        int mid = (l + r) >> 1;
        ls(rt) = build(l, mid);
        rs(rt) = build(mid + 1, r);
        return rt;
    }
    
    inline int update(int pre, int l, int r, int x){
        int rt = ++cnt;
        ls(rt) = ls(pre), rs(rt) = rs(pre), t[rt].sum = t[pre].sum + 1;
        if(l == r) return rt;
        int mid = (l + r) >> 1;
        if(x <= mid) ls(rt) = update(ls(pre), l, mid, x);
        else rs(rt) = update(rs(pre), mid + 1, r, x);
        return rt;
    }
    
    inline int query(int L, int R, int k, int l, int r){
        if(l == r) return l;
        int res = t[ls(R)].sum - t[ls(L)].sum;
        int mid = (l + r) >> 1;
        if(res >= k) return query(ls(L), ls(R), k, l, mid);
        else return query(rs(L), rs(R), k - res, mid + 1, r);
    }
    
    int main(){
        n = read(), m = read();
        for(int i = 1; i <= n; ++i) a[i] = read(), b[i] = a[i];
        sort(b + 1, b + 1 + n);
        int tot = unique(b + 1, b + 1 + n) - b - 1;
        for(int i = 1; i <= n; ++i)
            a[i] = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
        t[0].rt = build(1, tot);
        for(int i = 1; i <= n; ++i) t[i].rt = update(t[i - 1].rt, 1, tot, a[i]);
        while(m--){
            int l = read(), r = read(), k = read();
            write(b[query(t[l - 1].rt, t[r].rt, k, 1, tot)]), puts("");
        }
        return 0;
    }
    

    \[\_EOF\_ \]

  • 相关阅读:
    springBoot Mybaits分页错误
    验证码的技术实现原理
    《参与感》----产品篇
    参与感三三法则
    MIUI 的参与感
    从 UI 交互角度说语音识别产品
    语音识别开放平台调研以及主要技术
    测试蓝牙回连技术
    测试语音遥控器语音聊天的坑
    测试语音遥控器扫描连接的要点
  • 原文地址:https://www.cnblogs.com/xixike/p/15736009.html
Copyright © 2011-2022 走看看