划分树是一种基于线段树的数据结构。主要用于快速求出(在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小
① [ 2 4 3 5 8 1 7 6 ] // 原数组 [2,7]找第2小④ [1] [2] [3] [4] [5] [6] [7] [8] // 一直迭代直到找到答案
/// 划分树入门 #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 */