记没排序的数组为UA(unsorted array,即原数组),排序数组SA(sorted array)
划分树的建树过程:将区间为[L,R]的UA,按SA[MID]来划分,如果比SA[MID]值小,则分到左子树,比其大,则分到右子树,并把在某个位置小于等于SA[MID]的个数记录下来,直到区间长度为1为止。
Query函数在做的一个工作将区间长度逐渐缩为1。以下以在[L,R]内找第CNT大的数为例说明。假设
S表示在[L,R]内有几个小于等于DATA[MID]的个数
SS表示在[tr[root].l,l-1]内有几个小于等于DATA[MID]的个数
当S>=CNT时,将L变为tr[root].l+SS,因为现在的S已经比CNT大了,还要SS那部分有什么用,
而将R变为tr[root].l+ss+s-1则相当于将下一次的S值减少1,以使S趋向于CNT
示意图:
当S<CNT时,将L变为mid+(l-tr[root].l-ss)+1,括号内的数是为了进一步缩小区间长度用的。因为在[tr[root].l , l-1 ]内比DATA[MID]大的数,在下一步显然不需要
将R变为mid+l-tr[root].l-ss+(r-l+1-s),括号内的数是为了确定新区间的长度。即在[L,R]内有几个比DATA[MID]大的数。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 100010
int sa[N],tree[20][N],sum[20][N];
//sum[i][j]表示i层,1~j里比sa[m]小的数
int n,m,t,x,y,k;
struct node
{
int l,r;
}root[N*4];
inline void build(int t,int d,int x,int y)
{
root[t].l=x;
root[t].r=y;
if(x==y) return;
int m=(x+y)>>1;
int lless=m-x+1;
for(int i=x;i<=y;i++)
{
if(tree[d][i]<sa[m]) lless--;
}
int lp=x,rp=m+1;
for(int i=x;i<=y;i++)
{
if(i==x) sum[d][i]=0;
else sum[d][i]=sum[d][i-1];
if(tree[d][i]<sa[m])
{
sum[d][i]++;
tree[d+1][lp++]=tree[d][i];
}
else if(tree[d][i]>sa[m])
{
tree[d+1][rp++]=tree[d][i];
}
else
{
if(lless>0)
{
sum[d][i]++;
lless--;
tree[d+1][lp++]=tree[d][i];
}
else
{
tree[d+1][rp++]=tree[d][i];
}
}
}
build(t*2,d+1,x,m);
build(t*2+1,d+1,m+1,y);
}
inline int query(int t,int d,int x,int y,int k)
{
if(x==y) return tree[d][x];
int ss,s;
int l=root[t].l,r=root[t].r,m=(l+r)>>1;
//ss:[l,x)之间被分到左子树的,s:[x,y]被分到左子树的
if(l==x)
{
ss=0;
s=sum[d][y];
}
else
{
ss=sum[d][x-1];
s=sum[d][y]-sum[d][x-1];
}
if(s>=k)
{
int xx=l+ss;//既然[x,y]的就够了,[l,x-1]就不用了里的ss个就没用了。
int yy=l+ss+s-1;//将下一次的s减1
return query(t*2,d+1,xx,yy,k);
}
else
{
int r1=x-l-ss;//[l,x-1]之间被分到右子树
int r2=y-x+1-s;//[x,y]之间被分到右子树
int xx=m+1 +r1;
int yy=m+1 +r1+r2-1;
return query(t*2+1,d+1,xx,yy,k-s);
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
scanf("%d",&sa[i]);
tree[0][i]=sa[i];
}
sort(sa+1,sa+n+1);
build(1,0,1,n);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",query(1,0,x,y,k));
}
}
return 0;
}