洛谷P3834 【模板】可持久化线段树 1(主席树) #主席树#

题目背景

这是个非常经典的主席树入门题——静态区间第 k 小

数据已经过加强,请使用主席树。同时请注意常数优化

题目描述

如题,给定 n 个整数构成的序列,将对于指定的闭区间查询其区间内的第 k 小值。

输入格式

第一行包含两个正整数 n,m,分别表示序列的长度和查询的个数。

第二行包含 n 个整数,表示这个序列各项的数字。

接下来 m 行每行包含三个整数 l,r,k , 表示查询区间 [l,r] 内的第 k 小值。

输出格式

输出包含 k 行,每行一个整数,依次表示每一次查询的结果

输入输出样例

输入 #1复制

5 5
25957 6405 15770 26287 26465 
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1

输出 #1复制

6405
15770
26287
25957
26287

说明/提示

数据范围

对于 20% 的数据满足:1≤n,m≤10

对于 50% 的数据满足:1≤n,m≤103

对于 80% 的数据满足:1≤n,m≤105

对于 100% 的数据满足:1≤n,m≤2×105

对于数列中的所有数 ai​,均满足 −109≤ai​≤109

样例数据说明

n=5,数列长度为 5,数列从第一项开始依次为[25957,6405,15770,26287,26465]

第一次查询为[2,2]区间内的第一小值,即为 6405

第二次查询为 [3,4] 区间内的第一小值,即为 15770

第三次查询为 [4,5] 区间内的第一小值,即为 26287

第四次查询为 [1,2] 区间内的第二小值,即为 25957

第五次查询为 [4,4] 区间内的第一小值,即为 26287

题解

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2e5 + 10;
int cnt, a[maxn], root[maxn];
struct node { int l, r, sum; } tree[maxn * 40];
vector<int> v;

inline const int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }
    return x * f;
}

inline int getid(int x)
{
    return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}

void update(int l, int r, int p, int pre, int& now)
{
    tree[now = ++cnt] = tree[pre];
    tree[now].sum++;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (p <= mid) update(l, mid, p, tree[pre].l, tree[now].l);
    else update(mid + 1, r, p, tree[pre].r, tree[now].r);
}

int query(int l, int r, int k, int L, int R)
{
    if (l == r) return l;
    int mid = (l + r) >> 1;
    int nl = tree[tree[R].l].sum - tree[tree[L].l].sum;
    return k <= nl ? query(l, mid, k, tree[L].l, tree[R].l) : query(mid + 1, r, k - nl, tree[L].r, tree[R].r);
}

int main()
{
    int n = read(), m = read();
    for (int i = 1; i <= n; i++) v.push_back(a[i] = read());
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    for (int i = 1; i <= n; i++) update(1, n, getid(a[i]), root[i - 1], root[i]);
    while (m--)
    {
        int l = read(), r = read(), k = read();
        printf("%d\n", v[query(1, n, k, root[l - 1], root[r]) - 1]);
    }
    return 0;
}
发布了367 篇原创文章 · 获赞 148 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_35850147/article/details/103265395