概述
题意:给定一个n个数的数组,有m个查询,每个查询为[a,b,k],意思是区间[a,b]的第k大数。
思路:由这个题认识了什么叫做划分树。划分树的基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数。查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,最后范围缩小到1,就找到了。
建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数a[mid],小于a[mid]的数划入他的左子树[l,mid-1],大于它的划入右子树[mid,r]。同时,对于第i个数,记录在[l,i]区间内有多少数被划入左子树。最后,对它的左子树区间[l,mid-1]和右子树区间[mid,r]递归的继续建树就可以了。
建树的时候要注意对于被分到同一子树的元素,元素间的相对位置不能改变。
查找的过程:查找深度为h,在大区间[L,R]中找小区间[left,right]中的第k元素。再看看他是如何工作的。我们的想法是,先判断[left,right]中第k元素在[L,R]的哪个子树中,然后找出对应的小区间和k,递归的进行查找,直到小区间的left = right为止。具体思路见代码中注释。
注意:num数组除了这种建立方法还可以在建树时以每个区间为单位建立(百度百科的方法),
if( i==l )num[c][i]=0;
else
num[c][i]=num[c][i-1];
这样建树在查询的时候代码能少写一些。
#include <cstdio>
#include <algorithm>
#define N 100005
using namespace std;
int s[N],t[25][N],num[25][N];
int n,m;
void build(int left,int right,int d){
int i,j,k,mid,ml,flag;
if(left == right)
return;
mid = (left+right)>>1;
ml = mid-left+1;
for(i = left;i<=right;i++)//注意是从left到right而不是到mid
if(t[d][i] < s[mid])
ml--;
j = left,k = mid+1;
for(i = left;i<=right;i++){
flag = 0;
if(t[d][i] < s[mid] || (t[d][i]==s[mid]&&ml)){//表示进入左子树
flag = 1;
if(t[d][i] == s[mid])//如果是和中位数相等的情况
ml--;
t[d+1][j++] = t[d][i];
}else
t[d+1][k++] = t[d][i];
num[d][i] = num[d][i-1]+flag;//记录进入左子树的个数
}
build(left, mid, d+1);
build(mid+1, right, d+1);
}
int query(int left,int right,int k,int L,int R,int d){
int mid,x,y,rx,ry,val;
if(left == right)
return t[d][left];
mid = (L+R)>>1;
x = num[d][left-1] - num[d][L-1];//[L,left)内去往左子树的个数
y = num[d][right] - num[d][L-1];//[L,right]内去往左子树的个数
val = y-x;//[left,right]内去往左子树的个数
rx = left - L - x;//[L,left)内去往右子树的个数
ry = right - L + 1 - y;//[L,right]内去往右子树的个数
if(k <= val)
return query(L+x, L+y-1, k, L, mid, d+1);
return query(mid+1+rx, mid+ry, k-val, mid+1, R, d+1);
}
int main(){
int i,j,k,x;
scanf("%d %d",&n,&m);
for(i = 1;i<=n;i++){
scanf("%d",&s[i]);
t[0][i] = s[i];
}
sort(s+1,s+1+n);
build(1,n,0);
for(i = 0;i<m;i++){
scanf("%d %d %d",&j,&k,&x);
printf("%dn",query(j,k,x,1,n,0));
}
}
/*
t数组值如下,四周序号表示下标
t 1 2 3 4 5 6 7
0 1 5 2 6 3 7 4
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
3 1 2 3 4 5 6 0
num数组值如下,四周序号表示下标
num 1 2 3 4 5 6 7
0 1 1 2 2 3 3 4
1 1 2 2 2 3 4 4
2 1 1 2 2 3 3 0
*/
最后
以上就是激昂西装为你收集整理的poj 2104 划分树(查询区间第k大数)的全部内容,希望文章能够帮你解决poj 2104 划分树(查询区间第k大数)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复