题意:
n个数,m次询问,每次输入 l,r,k 。求区间 l,r 中第 k 小的数。
主席树讲解:http://www.cnblogs.com/zyf0163/p/4749042.html
void build(int x , int l , int r , int &p)
这里的x 就是该元素在原序列中的位置,p它在线段树中的位置,
l是左端点,r是右端点,
在这里 p 加了&(取地址符)这也就是说,在进行递归构建线段树的过程中,
p传递的元素会改变,所以就不用特意去维护了
时间复杂度:o(nlogn)o(nlogn)
空间复杂度:o(nlogn)
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<bitset>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define mod 1000000007
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct node
{
int l,r;
int sum;
}tr[maxn*40];
int n,m,a[maxn],b[maxn],root[maxn],tot = 0;
void build(int x,int l,int r,int &p) //构建线段树,&取地址符
{ //x 插入元素在原序列中的位置,p 插入元素再线段树中的位置
tr[++tot] = tr[p];
p = tot;
tr[p].sum++; //sum记录的是:相对于这个节点,有多少个比ta大的数
if(l==r)
return ;
int mid = (l+r)>>1;
if(x<=mid)
build(x,l,mid,tr[p].l); //该元素在线段树的左儿子中
else
build(x,mid+1,r,tr[p].r); //该元素在线段树的右儿子中
}
int query(int L,int R,int k,int l,int r) //查询[L,R]中第k小的数
{
if(l==r)
return l;
int val = tr[tr[R].l].sum-tr[tr[L].l].sum;
int mid = (l+r)>>1;
if(k<=val)
return query(tr[L].l, tr[R].l, k, l, mid);
else
return query(tr[L].r, tr[R].r, k-val, mid+1, r); //注意 k-val !!!
}
int main()
{
int l,r,k;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(b+1,b+n+1);
for(int i=1;i<=n;i++) //离散化(适应于无重复数)
a[i] = lower_bound(b+1,b+n+1,a[i])-b;
for(int i=1;i<=n;i++)
{
root[i] = root[i-1]; //记录一下根
build(a[i],1,n,root[i]);
}
while(m--)
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[query(root[l-1],root[r],k,1,n)]);
}
return 0;
}