K-th Number POJ - 2104 (主席树入门题)
题目链接
题目大意:n个数,m个查询,查询区间第k小值。
题目分析:主席树的入门题,关键就第i个线段树的节点存的元素是第1 ~ i 个元素,然后要查询[l, r]区间,只需要用第r棵线段树减去第l - 1 棵线段树(节点对应相减),然后节点里面剩下的就是要求的[l, r]区间的,然后第k小只要找到对应的第k个就ok了。
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 100;
int sorted[maxn], data[maxn], cnt, root[maxn];
struct node {
int sum, l, r;
} tree[maxn * 40];
// sum是当前区间数的个数, l是当前区间的左边节点的编号,r是右边节点的编号。
void init() {
cnt = 0;
}
//离散化获得id
int GetId(int k, int num) {
return lower_bound(sorted + 1, sorted + 1 + num, k) - sorted;
}
//根据旧版本的线段树创建新线段树的节点
int CreatNode(int pre) {
int idx = ++cnt;
tree[idx].sum = tree[pre].sum + 1;
tree[idx].l = tree[pre].l;
tree[idx].r = tree[pre].r;
return idx;
}
//建树
void build(int &root, int pre, int l, int r, int k) {
root = CreatNode(pre);
if(l == r) return ;
int m = (l + r) / 2;
if(k <= m) build(tree[root].l, tree[pre].l, l, m, k);
else build(tree[root].r, tree[pre].r, m + 1, r, k);
}
//查找
int query(int s, int e, int l, int r, int k) {
if(l == r) return l;
int sum = tree[tree[e].l].sum - tree[tree[s].l].sum;
int m = (l + r) / 2;
if(k <= sum) return query(tree[s].l, tree[e].l, l, m, k);
else return query(tree[s].r, tree[e].r, m + 1, r, k - sum);
}
int main()
{
int n, m, num;
while(~scanf("%d %d", &n, &m)) {
init();
for(int i = 1; i <= n; i++) {
scanf("%d", &data[i]);
sorted[i] = data[i];
}
sort(sorted + 1, sorted + n + 1);
num = unique(sorted + 1, sorted + n + 1) - sorted - 1;
for(int i = 1; i <= n; i++) {
build(root[i], root[i - 1], 1, num, GetId(data[i], num));
}
for(int i = 0; i < m; i++) {
int l, r, k;
scanf("%d %d %d", &l, &r, &k);
int v = query(root[l - 1], root[r], 1, num, k);
printf("%d\n", sorted[v]);
}
}
}