题意:
给n个数,q次询问。每次求出指定区间(x,y)之间排序后第k个数。
考虑到n的范围是10^5,m的范围是5*10^4.常规方法必然超时。考虑使用树结构来存储每一段区间(按线段树建树方式划分和查找,提高效率)。总的来讲,就是把数组用归并排序,因为我们希望得到每个二区间的原数与有序数。再用线段树存下这个归并排序的过程(称其为归并树)。对于每一次询问,二分枚举1-n中的数来检验是否是该区间的第k个数。不用担心一种情况:枚举到m,且m满足在该区间有k-1个数比他小,但m却不是这个区间内的值。这个问题,在二分过程中可以很好避免。自己想咯。
代码:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<string.h>
#include<string>
#include<map>
using namespace std;
const int N = 100000+10;
int n,q,rk;
struct node
{
int left,right;
int depth;
};
node seg_tree[N*4];
int a[N],aa[N];
int b[100][N];
void creattree(int l,int r,int rt,int depth)
{
seg_tree[rt].left = l;
seg_tree[rt].right = r;
seg_tree[rt].depth = depth;
if(l==r)
{
b[depth][l]=a[l];
return;
}
int mid = (l+r)/2;
creattree(l,mid,rt<<1,depth+1);
creattree(mid+1,r,rt<<1|1,depth+1);
int ps=l,qs=mid+1;
int k=l;
while(ps<=mid || qs<=r)
{
if((ps>mid)||(qs<=r && a[ps]>a[qs]))
b[depth][k++] = a[qs++];
else
b[depth][k++] = a[ps++];
}
for(int i=l;i<=r;i++)
a[i]=b[depth][i];
}
int query(int l,int r,int rt,int key)
{
if(l==seg_tree[rt].left && r==seg_tree[rt].right)
{
int dep = seg_tree[rt].depth;
int res = 0;
int L = seg_tree[rt].left,R=seg_tree[rt].right;
res = upper_bound(&b[dep][L],&b[dep][R]+1,key)-&b[dep][L];
return res;
}
int mid = (seg_tree[rt].left+seg_tree[rt].right)/2;
if(mid>=r)
return query(l,r,rt<<1,key);
else if(mid+1<=l)
return query(l,r,rt<<1|1,key);
else
return query(l,mid,rt<<1,key) + query(mid+1,r,rt<<1|1,key);
}
int main()
{
while(scanf("%d%d",&n,&q)==2)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
creattree(1,n,1,1);
while(q--)
{
int x,y;
scanf("%d%d%d",&x,&y,&rk);
int L=1,R=n,mid;
int ans=R;
while(L<=R)
{
mid = (L+R)/2;
int kth = query(x,y,1,a[mid]);
if(kth>=rk)
R=mid-1;
else
L=mid+1;
}
ans = a[L];
printf("%d\n",ans);
}
}
return 0;
}