poj 2104 划分树(查询区间第k大数)

来源:互联网 发布:搭建高并发java框架 编辑:程序博客网 时间:2024/06/06 01:42

题意:给定一个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 100005using 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("%d\n",query(j,k,x,1,n,0));    }}/*t数组值如下,四周序号表示下标t 1 2 3 4 5 6 70 1 5 2 6 3 7 41 1 2 3 4 5 6 72 1 2 3 4 5 6 73 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*/


0 0
原创粉丝点击