主席树学习

主席树 是函数式线段树,是一种持久化数据结构。对于每次修改,我们只需该次修改所改动的结点新建一遍,其余的结点跟上一历史版本一样即可。如果每次都是单点修改的话,那么就只会增加 (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;
}
原文地址:https://www.cnblogs.com/solvit/p/11336338.html