维护不带修改的区间第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;
}