第K小的数

第K小的数

维护不带修改的区间第k小,分块,划分树都可以做。

然而最常用的是以下做法:主席树或可持久化线段树(我也不知道二者有什么差别。

对于每个区间[1,i]建立一棵权值线段树,显然它与[1,i-1]的差别只有一条链,那么只要建立那条链上的节点就可以了。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <cstdio>
#include <cctype>
#include <algorithm>


const int N = 100000 + 10;


int a[N], b[N], root[N], s[N * 40], ls[N * 40], rs[N * 40], tot;


void amend(int &, int , int , int , int );
int query(int , int , int , int , int );
int read(void);


int main(void)
{
    int n = read(), m = read();
    for (int i = 1; i <= n; ++i)
        a[i] = b[i] = read();
    std::sort(b + 1, b + n + 1);
    int num = std::unique(b + 1, b + n + 1) - b;
    for (int i = 1; i <= n; ++i)
        amend(root[i], root[i - 1], 1, num, std::lower_bound(b + 1, b + num, a[i]) - b);
    while (m--)
    {
        int i = read(), j = read(), k = read();
        printf("%d\n", b[query(root[i - 1], root[j], 1, num, k)]);
    }
    return 0;
}


void amend(int &cur, int lst, int l, int r, int val)
{
    cur = ++tot;
    s[cur] = s[lst] + 1;
    ls[cur] = ls[lst];
    rs[cur] = rs[lst];
    if (l < r - 1)
    {
        if (val < (l + r) / 2)
            amend(ls[cur], ls[lst], l, (l + r) / 2, val);
        else
            amend(rs[cur], rs[lst], (l + r) / 2, r, val);
    }
}


int query(int L, int R, int l, int r, int k)
{
    if (l == r - 1)
        return l;
    int tmp = s[ls[R]] - s[ls[L]];
    if (k <= tmp)
        return query(ls[L], ls[R], l, (l + r) / 2, k);
    return query(rs[L], rs[R], (l + r) / 2, r, k - tmp);
}


inline int read(void)
{
    char x;
    while (x = getchar(), x != '-' && !isdigit(x))
        ;
    bool flag = x == '-';
    int num = (flag ? getchar() : x) - '0';
    while (isdigit(x = getchar()))
        (num *= 10) += x - '0';
    return flag ? -num : num;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注