划分树的基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数。查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,最后范围缩小到1,就找到了。
建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数sorted[mid],小于sorted[mid]的数划入他的左子树,大于它的划入右子树。同时,对于第i个数,记录在[l,i]区间内有多少数被划入左子树。最后,对它的左子树区间和右子树区间递归的继续建树就可以了。
tree[20][maxn]//表示每层每个位置的值
sorted[maxn]//已经排序好的数
!!!! toleft[20][maxn]//toleft[dep][i]表示第dep层区间[1,i]有多少个数划分到左子树
在这个过程中,数的相对位置不发生变化
sorted: [1 2 3 4 5 6 7]
tree[0]: [1 5 2 3 4 6 7] topleft[0] [1,1,2,3,4,4,4]
tree[1]: [1 2 3 4] [ 5 6 7] topleft[1] [1,2,2,2,3,4,4]
tree[2]: [1 2] [3 4] [5 6][0] topleft[2] [1,1,2,2,3,3,0]
tree[3]: [0] [0] [0] [0] [0] [0] [0] topleft[3] [0,0,0,0,0,0,0]
//hdu 2665
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<map>
#include<vector>
using namespace std;
int n,m;
int tree[20][100010];
int sorted[100010];
int toleft[20][100010];
void build(int l,int r,int dep)
{
if(l==r)//已经到达最后一层
return;
int mid=(l+r)>>1;
int same=mid-l+1;//表示等于中间值且被分为左边的个数
for(int i=l;i<=r;i++)
{
if(tree[dep][i]<sorted[mid])
same--;
}
int lpos=l;
int rpos=mid+1;
for(int i=l;i<=r;i++)
{
if(tree[dep][i]<sorted[mid])
tree[dep+1][lpos++]=tree[dep][i];
else if(tree[dep][i]==sorted[mid]&&same>0)//看他是否被分到左边
{
tree[dep+1][lpos++]=tree[dep][i];
same--;
}
else
tree[dep+1][rpos++]=tree[dep][i];
toleft[dep][i]=toleft[dep][l-1]+lpos-l;//记录区间被分到左边值的个数
}
build(l,mid,dep+1);
build(mid+1,r,dep+1);
}
int query(int L,int R,int l,int r,int dep,int k)
{
if(l==r)
return tree[dep][l];
int mid=(L+R)>>1;
int cnt=toleft[dep][r]-toleft[dep][l-1];
if(cnt>=k)//在左子树上
{
int newl=L+toleft[dep][l-1]-toleft[dep][L-1];//在往左右子树分配的时候相对位置是不会发生变化的,toleft[dep][l-1]-toleft[dep][L-1]不在区间内,就除去它们,得到新区间的左边界
int newr=newl+cnt-1;//右边界
return query(L,mid,newl,newr,dep+1,k);
}
else
{
int newr=r+toleft[dep][R]-toleft[dep][r];//toleft[dep][R]-toleft[dep][r]这一部分被分到了左区间,原来在r的右边现在到r的左边,所以r的值增大toleft[dep][R]-toleft[dep][r],得到新区间的右边界
int newl=newr-(r-l-cnt);//左边界[l+cnt,r],[newl,newr],本来就是相同的区间,只不过是右边一部分数跑到了左边,是边界的位置发生了变化
return query(mid+1,R,newl,newr,dep+1,k-cnt);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(tree,0,sizeof(tree));
memset(toleft,0,sizeof(toleft));
for(int i=1; i<=n; i++)
{
scanf("%d",&tree[0][i]);
sorted[i]=tree[0][i];
}
sort(sorted+1,sorted+n+1);
build(1,n,0);//建树
int l,r,k;
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",query(1,n,l,r,0,k));
}
}
}