题意: 给一个数组,对其进行多次查询。每次查询为(L,R,K),即在闭区间内[L,R]第K大的数。
思路:本来没什么思路的,然后学长说这是划分树的模板题,然后就没有然后了......
说一下自己对划分树的理解:
个人感觉划分树就是线段树的一种,每一个节点对应一段区间,节点内有两个数组num[],ans[]。
num[]为对应线段内的所有数据,按输入数据存放。
ans[]内存放的信息为对应线段的[ L, i ]内有多少个数被放到了其左子树中, L 为对应线段的最左边。
对于一段区间,首先找出该区间内的中位数,小于该数的放到其左子树中,大于该数的要放到右子树中。
若存在与中位数相同的数,则要按左右子树中数的个数来讨论,最终要保证左右子树数据量之差不超过 1。
中位数要放到那则可根据自己写线段树时的习惯而定。
查询过程:
对于给定的(L,R,K),判断[L,R]中有多少个数被放到了左子树中,总而得出第K个数被放到了左子树还
是右子树,总而确定下一次递归查询的范围。
每一次递归都要改变(L,R,K),直到L == R。
当 L == R时,节点内存住的数即为答案,递归查询结束。
其实,整个划分树的建立即为一个可以看作一次快速排序的过程,线段树的叶子节点从左到右即为按照一定规则排完序之后的序列。
下图中每一条线段托住的即为线段树中一个节点内的信息,上面为 对应的节点内ans[] 的信息。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;
int num[101000],s[101000];
int stn[25][101000],ans[25][101000];
struct N
{
int l,r;
} st[401000];
bool cmp(int a,int b)
{
return a < b;
}
void Init_St(int site,int l,int r,int h)
{
if(l == r)
{
st[site].l = l,st[site].r = r;
stn[h][l] = s[l];
return;
}
st[site].l = l,st[site].r = r;
int mid = (l+r)>>1 , i;
int t1,t2;
for(i = st[site].l,t1 = st[site].l,t2 = mid+1; i <= st[site].r ; ++i)
{
if(i == st[site].l)
{
if(stn[h][i] <= s[mid])
{
ans[h][i] = 1;
stn[h+1][t1++] = stn[h][i];
}
else
{
ans[h][i] = 0;
stn[h+1][t2++] = stn[h][i];
}
}
else
{
if(stn[h][i] <= s[mid])
{
ans[h][i] = ans[h][i-1]+1;
stn[h+1][t1++] = stn[h][i];
}
else
{
ans[h][i] = ans[h][i-1];
stn[h+1][t2++] = stn[h][i];
}
}
}
Init_St(site<<1,l,mid,h+1);
Init_St(site<<1|1,mid+1,r,h+1);
}
int query(int site,int l,int r,int k,int h)
{
if(l == r)
{
return stn[h][l];
}
int mid = (st[site].l + st[site].r)>>1;
if(k <= ans[h][r] - (l != st[site].l ? ans[h][l-1] : 0))
{
return query(site<<1,(l == st[site].l ? l : st[site].l+ans[h][l-1]),st[site].l-1+ans[h][r],k,h+1);
}
else
{
return query(site<<1|1,(l == st[site].l ? mid+1 : mid+l-st[site].l+1-ans[h][l-1]),mid+r-st[site].l+1-ans[h][r],k-(ans[h][r]-(l == st[site].l ? 0 : ans[h][l-1])),h+1);
}
}
int main()
{
int i,n,m,l,r,k;
while(scanf("%d %d",&n,&m) != EOF)
{
for(i = 1; i <= n; ++i)
{
scanf("%d",&num[i]);
s[i] = num[i];
}
sort(s+1,s+n+1,cmp);
for(i = 1; i <= n; ++i)
{
stn[0][i] = num[i];
}
Init_St(1,1,n,0);
while(m--)
{
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",query(1,l,r,k,0));
}
}
return 0;
}