题意:给一个数组,问区间(L,R)内的第k小个数字,就是把(L,R)内的元素从小到大排序,取第k个数。
主席树的模板题,但是主席树确实很难想,而且也不是很好理解。代码也是抄的大佬的。。。
大概的结构是当n棵权值线段树,每次加入一个数,就要建立一个独立的权值线段树,但是对于这个新建立的权值线段树的左右子树,如果和前一棵树相同就不再建立新的,而是直接沿用前一棵树的。如果没有能用的子树,就只能建立新的结点。
查询第k个数字的时候,如果左区间的数字个数大于等于k就递归左子树,找第k个数,如果小于k就递归右子树,找第(k-区间左右端点的左子树的结点个数之差)个数。
注意:函数参数较多,传参一定要弄对,update函数中的pos和query中的不一样,在update中不管是创建左子树还是右子树,都是第pos个数,而在query中如果是右子树,应该是pos-num个数
#pragma warning(disable:4996)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
int num[MAXN], sorted[MAXN], cnt, root[MAXN];
//保存排序前的数组,排序后的数组,每个权值线段树根结点
struct node
{
int sum, lson, rson;
};
node tree[MAXN<<5];
//tree要开大一点,否则会RE
int create(int sumnum, int Lson, int Rson)
{
//每一个结点有一个编号
int id = ++cnt;
tree[id].lson = Lson;
tree[id].rson = Rson;
tree[id].sum = sumnum;
return id;
}
void update(int& rt, int pre_rt,int pos, int L, int R)
{
//创建结点
rt = create(tree[pre_rt].sum + 1, tree[pre_rt].lson, tree[pre_rt].rson);
if (L == R)return;
int mid = (L + R) >> 1;
//创建结点的左儿子或者右儿子
if (pos <= mid)update(tree[rt].lson, tree[pre_rt].lson, pos, L, mid);
else update(tree[rt].rson, tree[pre_rt].rson, pos, mid + 1, R);
}
int query(int st,int ed,int L,int R,int k)
{
//递归到叶子结点,返回排序后数组下标
if (L == R)return L;
int mid = (L + R) >> 1,num;
//区间左右端点的左子树的结点个数之差
num = tree[tree[ed].lson].sum - tree[tree[st].lson].sum;
//递归左子树,找第k个数字
if (k <= num)return query(tree[st].lson, tree[ed].lson, L, mid, k);
//递归右子树,找第k-num个数字
else return query(tree[st].rson, tree[ed].rson, mid + 1, R, k - num);
}
int main()
{
int n, m, i, j, sum, pos;
while (scanf("%d%d", &n, &m) == 2)
{
cnt = 0;
memset(tree, 0, sizeof(tree));
memset(root, 0, sizeof(root));
for (i = 1;i <= n;i++)
{
scanf("%d", &num[i]);
sorted[i] = num[i];
}
sort(sorted + 1, sorted + 1 + n);
sum = unique(sorted + 1, sorted + 1 + n) - sorted-1;
//排序去重,离散化
for (i = 1;i <= n;i++)
{
//在排序后数组中的位置
pos = lower_bound(sorted + 1, sorted + 1 + sum, num[i]) - sorted;
update(root[i], root[i - 1], pos, 1, sum);
}
int t1, t2, t3;
while (m--)
{
//查找(L,R)区间,即第R棵权值线段树和第L-1棵权值线段树之差。
scanf("%d%d%d", &t1, &t2, &t3);
printf("%d\n", sorted[query(root[t1 - 1], root[t2], 1, sum, t3)]);
}
}
return 0;
}