划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时间复杂度内)序列区间的第k大值
划分树的基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数。查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,最后范围缩小到1,就找到了。
划分树定义为,她的每一个节点保存区间 [lft, rht] 所有元素,元素排列顺序与原数组(输入)相同,但是,
两个子树的元素为该节点所有元素排序后( rht − lft + 1 )/ 2个进入左子树,其余的到右子树,同时维护一个
num域,num[i] 表示lft → i 这些点有多少进入了左子树。(摘自某牛人的博客)
划分树的建立和查找简单举例:
在 [ 2 4 3 5 8 1 7 6 ] 中找区间[2,7]的第2小
①②
③
④
/// 划分树入门
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=100000+123;
int sorted[maxn];
struct node{
int v[maxn], num[maxn];
}T[21];
void build(int l, int r, int p)
{/// 生成当前层的num以及下一层的v
if(l==r)return;
int mid=(l+r)>>1;
int isame=mid+1-l, same=0;/// 貌似没用。。。其实很有用。。。
for (int i=l; i<=r; ++i)
if(T[p].v[i]<sorted[mid])same++;
int ll=l, rr=mid+1;
for (int i=l; i<=r; ++i)
{
if(i==l)/// 左端初始为0
{
T[p].num[i]=0;
}else /// initialization
{
T[p].num[i]=T[p].num[i-1];
}
if(T[p].v[i]<sorted[mid])
{
T[p].num[i]++;
T[p+1].v[ll++]=T[p].v[i];
}else
if(T[p].v[i]>sorted[mid])
{
T[p+1].v[rr++]=T[p].v[i];
}else
{
if(same<isame)
{
same++;
T[p].num[i]++;
T[p+1].v[ll++]=T[p].v[i];
}else
{
T[p+1].v[rr++]=T[p].v[i];
}
}
}
build(l, mid, p+1);
build(mid+1, r, p+1);
}
int Query(int a, int b, int k, int p, int l, int r)
{
if(l==r)return T[p].v[a];
int la=(a==l)?0:T[p].num[a-1], mid=(l+r)>>1;///区间内a之前的左子树元素个数
// printf("Q:%d %d\n", p, la);
int s=T[p].num[b]-la;/// 【a,b】间的左子树元素个数
if(s>=k)/// 左子树元素大于所要找的第k个元素的时候
{
a=l+la;
b=l+la+s-1;
// printf("l:a==%d b==%d l==%d r==%d k=%d\n", a, b, l, mid, k);
return Query(a, b, k, p+1, l, mid);
}else
{
a=mid+a-l-la+1;
b=b+mid+a-l-la-a+1-s;
// printf("r:a==%d b==%d l==%d r==%d k=%d\n", a, b, mid+1, r, k-s);
return Query(a, b, k-s, p+1, mid+1, r);
}
}
int main ()
{
int n;
int cas=0;
while (~scanf("%d", &n))
{
printf("Case %d:\n", ++cas);
for (int i=0; i<n; ++i)
{
scanf("%d", &T[0].v[i]);
sorted[i]=T[0].v[i];
}
sort(sorted, sorted+n);
build(0, n-1, 0);
// for (int i=0; i<5; ++i)
// {
// for (int j=0; j<n; ++j)
// {
// printf("v=%d\t", T[i].v[j]);
// }
// puts("");
// for (int j=0; j<n; ++j)
// printf("m=%d\t", T[i].num[j]);
// puts("");
// }
int m; scanf("%d", &m);
for (int i=0; i<m; ++i)
{
int a, b; scanf("%d%d", &a, &b);
printf("%d\n", Query(a-1, b-1, (b-a)/2+1, 0, 0, n-1));
}
}
return 0;
}
/*
5
5 3 2 4 1
3
1 3
2 4
3 5
5
10 6 4 8 2
3
1 3
2 4
3 5
8
1 5 6 3 8 4 4 2
3
1 7
2 6
3 5
*/