数据结构 - 主席树

查询 $[l,r]$ 区间第 $k$ 小的值。

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e5;  //数据范围
int tot, n, m;
int sum[(maxn << 5) + 10], rt[maxn + 10], ls[(maxn << 5) + 10],
    rs[(maxn << 5) + 10];
int a[maxn + 10], ind[maxn + 10], len;
inline int getid(const int &val) { //离散化
    return lower_bound(ind + 1, ind + len + 1, val) - ind;
}
int build(int l, int r) { //建树
    int root = ++tot;
    if (l == r)
        return root;
    int mid = l + r >> 1;
    ls[root] = build(l, mid);
    rs[root] = build(mid + 1, r);
    return root;  //返回该子树的根节点
}
int update(int k, int l, int r, int root) { //插入操作
    int dir = ++tot;
    ls[dir] = ls[root], rs[dir] = rs[root], sum[dir] = sum[root] + 1;
    if (l == r)
        return dir;
    int mid = l + r >> 1;
    if (k <= mid)
        ls[dir] = update(k, l, mid, ls[dir]);
    else
        rs[dir] = update(k, mid + 1, r, rs[dir]);
    return dir;
}
int query(int u, int v, int l, int r, int k) { //查询操作
    int mid = l + r >> 1,
        x = sum[ls[v]] - sum[ls[u]];  //通过区间减法得到左儿子的信息
    if (l == r)
        return l;
    if (k <= x)  //说明在左儿子中
        return query(ls[u], ls[v], l, mid, k);
    else  //说明在右儿子中
        return query(rs[u], rs[v], mid + 1, r, k - x);
}
inline void init() {
    scanf("%d%d", &n, &m);
    for (register int i = 1; i <= n; ++i)
        scanf("%d", a + i);
    memcpy(ind, a, sizeof ind);
    sort(ind + 1, ind + n + 1);
    len = unique(ind + 1, ind + n + 1) - ind - 1;
    rt[0] = build(1, len);
    for (register int i = 1; i <= n; ++i)
        rt[i] = update(getid(a[i]), 1, len, rt[i - 1]);
}
int l, r, k;
inline void work() {
    while (m--) {
        scanf("%d%d%d", &l, &r, &k);
        printf("%d\n", ind[query(rt[l - 1], rt[r], 1, len, k)]);  //回答询问
    }
}
int main() {
    init();
    work();
    return 0;
}

 

posted @ 2019-03-04 20:21  韵意  阅读(191)  评论(0编辑  收藏  举报