题目链接
题意
给定数组、无修改、多查询,求区间第k小。
思路
主席树板子题
先离散化,在对每个节点建线段树,线段树存储下标位置+1的前缀和。
每次查询,查询俩节点之间线段树的差值第k小。
感觉代码思路理解不是很难,和树上每个节点建树差别不大?,但是用好写题不容易,太菜了。
时间复杂度应该是
O
(
(
n
+
m
)
l
o
g
n
)
O((n+m)logn)
O((n+m)logn) ,空间复杂度应该是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),不知道对不对。
代码
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void debug() { cout << endl; }
template<typename T, typename ...R> void debug (T f, R ...r) { cout << "[" << f << "]"; debug (r...); }
const int N = 1e5+5;
int n, q, id, root[N], a[N];
struct Node
{
int l, r, sum;
}t[N*20];
vector <int> lis;
int getlis(int x)
{
return lower_bound(lis.begin(), lis.end(), x) - lis.begin();
}
void build(int l, int r, int &x, int y, int pos)
{
t[++id] = t[y];
++t[id].sum;
x = id;
if(l == r) return;
int mid = l+r >> 1;
if(pos <= mid) build(l, mid, t[x].l, t[y].l, pos);
else build(mid+1, r, t[x].r, t[y].r, pos);
}
int query(int l, int r, int x, int y, int pos)
{
if(l == r) return l;
int mid = l+r >> 1, tmp = t[t[y].l].sum - t[t[x].l].sum;
if(tmp >= pos) return query(l,mid,t[x].l,t[y].l,pos);
else return query(mid+1,r,t[x].r,t[y].r,pos-tmp);
}
int main()
{
scanf("%d%d",&n,&q);
for(int i = 1; i <= n; ++i) scanf("%d",&a[i]), lis.push_back(a[i]);
sort(lis.begin(), lis.end());
lis.erase(unique(lis.begin(), lis.end()), lis.end());
lis.insert(lis.begin(),lis[0]-1);
for(int i = 1; i <= n; ++i) build(1,n,root[i],root[i-1],getlis(a[i]));
while(q--)
{
int l, r, x;
scanf("%d%d%d",&l,&r,&x);
printf("%d\n",lis[query(1,n,root[l-1],root[r],x)]);
}
return 0;
}