1 //看了主席树,初步理解可持久化线段树,是用于查找不同区间,利用不同插入值的时候的线段树,
2 //因为每个线段树查找都只能是【1,maxn】
3 //所以如果要查找【l,r】,那么把【1,r】-【1,l-1】作为每个节点,这就是看作从r的线段树中剥夺了l-1的节点
4 //实现方法是要用dt建树(动态),在最开始的树上添加链条,这样不要造新树,毕竟o【4*n】
5 //权值和二分都是【1,maxn】查询第k小的必要方法
6 //题目对应:洛谷p3834
7 //维护排序后元素的出现次数,利用前缀和得到第k小
8
9 #include<cstdio>
10 #include<cstring>
11 #include<algorithm>
12 #define mid (l+r)/2
13 using namespace std;
14
15 const int N = 200010;
16 int n, q, m, cnt = 0;
17 int a[N], b[N], T[N];//a:初始数组
18 //b:排序后数组,使用其下标作为线段树叶子结点
19 //T:第几棵树的root
20 int sum[N<<5], L[N<<5], R[N<<5];
21 //sum代表该节点区间的叶子数
22 inline int build(int l, int r)//建造一个n大小的线段树
23 {
24 int rt = ++ cnt;//这里记录的是第几个节点,我们要把第几个节点和代表区间清楚分开!
25 sum[rt] = 0;//初始建树,都是0
26 if (l < r)
27 {
28 L[rt] = build(l, mid);//左右遍历
29 R[rt] = build(mid+1, r);
30 }
31 return rt;
32 }
33
34 inline int update(int pre, int l, int r, int x)//其实也就是动态开点的过程
35 {
36 int rt = ++ cnt;//这里记录的是第几个节点,我们要把第几个节点和代表区间清楚分开!
37
38 L[rt] = L[pre];
39 R[rt] = R[pre];
40 sum[rt] = sum[pre]+1;
41
42 if (l < r)//找到x这个叶子,一路上包含他的都+1即可
43 {
44 if (x <= mid)//寻找节点所在左右树,和query不同,因为x是叶子结点的数,也就是区间中的一个数
45 L[rt] = update(L[pre], l, mid, x);
46 else
47 R[rt] = update(R[pre], mid+1, r, x);
48 }
49 return rt;
50 }
51
52 inline int query(int u, int v, int l, int r, int k)
53 {
54 if (l >= r)
55 return l;
56 int leftsum = sum[L[v]] - sum[L[u]];//剥夺操作
57 //与quanzhi—--erfen_line_tree中
58 //k和tree[root*2]比较异曲同工
59 if (k<=leftsum)
60 return query(L[u], L[v], l, mid, k);
61 else
62 return query(R[u], R[v], mid+1, r, k-leftsum);
63 }
64
65 int main()
66 {
67 scanf("%d%d", &n, &q);
68 for (int i = 1; i <= n; i ++)
69 {
70 scanf("%d", &a[i]);
71 b[i] = a[i];
72 }//输入所有的数
73 sort(b+1, b+1+n);//因为权值线段树需要排序的
74 m = unique(b+1, b+1+n)-b-1;//这是为了得到一共有多少不重复的值,这是叶子结点数量,也是m大小的初始线段树
75 //unique的返回值是第一个重复的所在,也是最后一位的后一位
76 T[0] = build(1, m);
77 //初始造树,之后每一颗树都是采取动态将多的需要的链接上去,来减少空间使用。
78 //至于为什么要分开?不始终那棵树都要4*n的空间吗?
79 //是的,初始树是4*n,但是可持久化需要m棵树,如果都新建树,那么将要m*4*n的空间
80 //对后来的树使用【动态开点】可以将每次添加的空间减小为O(m*logn)
81 //这样一共就【4*n+(m-1)*logn】
82 //这解答了我2022.3.14日一整晚对于线段树动态开点的困惑,动态开点是用于多树情况的
83
84 for (int i = 1; i <= n; i ++){
85
86 int t = lower_bound(b+1, b+1+m, a[i])-b;//寻找元素在b中的排序
87 //将使用b的序号建树,可以减少空余的叶子结点
88 //返回的时候返回b【t】即可
89 T[i] = update(T[i-1], 1, m, t);
90 //T代表第几棵树,在上一棵树的基础上添加链,T的值代表
91 //该棵树的root值
92 }
93
94 while (q --){
95 int x, y, z;
96 scanf("%d%d%d", &x, &y, &z);
97 int t = query(T[x-1], T[y], 1, m, z);//寻找【x,y】即【1,y】-【1,x-1】
98 printf("%d\n", b[t]);
99 }
100 return 0;
101 }