• 主席树学习


    主席树 是函数式线段树,是一种持久化数据结构。对于每次修改,我们只需该次修改所改动的结点新建一遍,其余的结点跟上一历史版本一样即可。如果每次都是单点修改的话,那么就只会增加 (log n) 个新结点。

    由于动态开点的原因,左右儿子就不能通过完全二叉树找左右儿子那种方式来找了,必须要记录自己的左右儿子编号

    假设当前主席树是前 (i−1) 次操作之后的状态,根为 (root[i−1]) ,一共有 (tot) 个结点,现在要进行第 (i) 次操作。

    • (root[i] = ++tot;pre = root[i-1];now=root[i];)
    • 如果修改点在左子树内,且 (pre e 0)(前 (i-1) 次操作后至少已经有一棵线段树),那么就令 (now) 的右儿子等于 (pre) 的右儿子。然后新建一个点作为 (now) 的左儿子,(pre=lson[pre];now=lson[now]) 。右子树同理。
    • 如果到这条修改路径的底端则回溯更新结点信息,否则重复第二步。

    (root[i]) 开始访问,就可以访问到 (1~i) 次操作之后的数据结构。

    题目

    #include<stdio.h>
    #include<algorithm>
    
    using namespace std;
    
    const int maxn = 100005;
    const int M = maxn * 40;
    int n, q, m, tot;
    int a[maxn], t[maxn], T[maxn];
    int lson[M], rson[M], sum[M];
    
    void init()
    {
        for(int i = 1; i <= n; i++) t[i] = a[i];
        sort(t + 1, t + n + 1);
        m = unique(t + 1, t + n + 1) - (t + 1);
    }
    int _hash(int x)
    {
        return lower_bound(t + 1, t + m + 1, x) - t;
    }
    int build(int l, int r)
    {
        int root = tot++;
        sum[root] = 0;
        if(l != r){
            int mid = (l + r) >> 1;
            lson[root] = build(l, mid);
            rson[root] = build(mid + 1, r);
        }
        return root;
    }
    int update(int root, int pos, int val)
    {
        int newroot = tot++, ret = newroot;
        sum[newroot] = sum[root] + val;
        int l = 1, r = m;
        while(l < r){
            int mid = (l + r) >> 1;
            if(pos <= mid){
                lson[newroot] = tot++; rson[newroot] = rson[root];
                newroot = lson[newroot]; root = lson[root];
                r = mid;
            }
            else{
                rson[newroot] = tot++; lson[newroot] = lson[root];
                newroot = rson[newroot]; root = rson[root];
                l = mid + 1;
            }
            sum[newroot] = sum[root] + val;
        }
        return ret;
    }
    int query(int left, int right, int k)
    {
        int l = 1, r = m;
        while(l < r){
            int mid = (l + r) >> 1;
            if(sum[lson[left]] - sum[lson[right]] >= k){
                r = mid;
                left = lson[left];
                right = lson[right];
            }
            else{
                l = mid + 1;
                k -= (sum[lson[left]] - sum[lson[right]]);
                left = rson[left];
                right = rson[right];
            }
        }
        return l;
    }
    int main()
    {
        while(~scanf("%d%d", &n, &q)){
            tot = 0;
            for(int i = 1; i <= n; i++){
                scanf("%d", &a[i]);
            }
            init();
            T[n + 1] = build(1, m);
            for(int i = n; i >= 1; i--){
                int pos = _hash(a[i]);
                T[i] = update(T[i + 1], pos, 1);
            }
            while(q--){
                int l, r, k;
                scanf("%d%d%d", &l, &r, &k);
                printf("%d
    ", t[query(T[l], T[r + 1], k)]);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Codeforces Round #407 (Div. 2)A B C 水 暴力 最大子序列和
    Codeforces Round #358 (Div. 2) A B C 水 水 dfs序+dp
    51nod 1225 数学
    HDU 4584 splay
    bzoj 1588 平衡树 splay
    HDU 4722 数位dp
    Kubernetes 自动伸缩 auto-scaling
    转发一篇能看懂的关于ingress的说明
    Http 安全检测
    mdadm 软RAID
  • 原文地址:https://www.cnblogs.com/solvit/p/11336338.html
Copyright © 2020-2023  润新知