线段树一维的刷差不多了,求区间第K数一直卡着。
划分树和归并树都可以求,比较了一下时间效率,划分树比归并树快了很多,而且POJ有个求区间第K数的题用归并树居然过不去。
鉴于时间短,我决定把划分树给弄明白= =。。借用下小HH的图。
划分树和归并树都是用线段树作为辅助的,原理是基于 快排 和 归并排序 的。
划分树的建树过程基本就是模拟快排过程,取一个已经排过序的区间中值,然后把小于中值的点放左边,大于的放右边。并且记录d层第i个数之前(包括i)小于中值的放在左边的数。具体看下面代码注释。
关键是询问过程,具体见图。优快云现在上传图片质量好差啊啊啊啊啊啊 。
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=100005;
const int DEPTH=20;
int less_mid[DEPTH][MAXN];
int seg[DEPTH][MAXN];
int data[MAXN];
int n;
struct seg_tree
{
int l,r;
void init(int tl,int tr)
{
l=tl,r=tr;
}
}tr[MAXN<<2];
void build(int l,int r,int root,int d)
{
tr[root].init(l,r);
if(l==r)
{
return ;
}
int mid=(l+r)>>1;
int lsame=mid-l+1;
int i;
for(i=l;i<=r;i++)
{
if(seg[d][i]<data[mid])
{
lsame--;
}
}
int lson=l,rson=mid+1,same=0;
for(i=l;i<=r;i++)
{
if(i==l)
{
less_mid[d][i]=0;
}
else
{
less_mid[d][i]=less_mid[d][i-1];
}
if(seg[d][i]<data[mid])
{
less_mid[d][i]++;
seg[d+1][lson++]=seg[d][i];
}
else if(seg[d][i]>data[mid])
{
seg[d+1][rson++]=seg[d][i];
}
else if(same<lsame)
{
same++;
less_mid[d][i]++;
seg[d+1][lson++]=seg[d][i];
}
else
{
seg[d+1][rson++]=seg[d][i];
}
}
build(l,mid,root<<1,d+1);
build(mid+1,r,root<<1|1,d+1);
}
int update(int l,int r,int root,int d,int k)
{
if(l==r)
{
return seg[d][l];
}
int s,ss;
if(l==tr[root].l)
{
s=less_mid[d][r];
ss=0;
}
else
{
s=less_mid[d][r]-less_mid[d][l-1];
ss=less_mid[d][l-1];
}
if(s>=k)
{
return update(tr[root].l+ss,tr[root].l+ss+s-1,root<<1,d+1,k);
}
else
{
int mid=(tr[root].l+tr[root].r)>>1;
int bb=l-tr[root].l-ss;
int b=r-l-s+1;
return update(mid+bb+1,mid+bb+b,root<<1|1,d+1,k-s);
}
}
int main()
{
int m;
while(~scanf("%d%d",&n,&m))
{
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&seg[1][i]);
data[i]=seg[1][i];
}
sort(data+1,data+1+n);
build(1,n,1,1);
int l,r,k;
while(m--)
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",update(l,r,1,1,k));
}
}
return 0;
}