poj 2104(线段树)

K-th Number
Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 45653   Accepted: 15177
Case Time Limit: 2000MS

Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?"
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 10 9 by their absolute values --- the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3

 

转自:http://blog.csdn.net/famousdt/article/details/7064866 
建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数a[mid],小于a[mid]的数划入它的左子树[l,mid-1],大于它的划入右子树[mid,r]。
同时,对于第i个数a[i],记录在[l,i]区间内有多少数被划入左子树。最后,对它的左子树区间[l,mid-1]和右子树区间[mid,r]递归的继续建树就可以了。
建树的时候要注意,对于被分到同一子树的元素,元素间的相对位置不能改变。

  查找的过程中主要问题就是确定将要查找的区间。
  查找深度为dep,在大区间[L ,R]中找小区间[l ,r]中的第k元素。
  我们的想法是,先判断[l ,r]中第k元素在[L ,R]的哪个子树中,然后找出对应的小区间和k,递归的进行查找,直到小区间的l==r为止。
  通过之前的记录可以知道,在区间[L,l-1]中有(toleft[dep][l-1]-toleft[dep][L-1])进入左子树,
  记它为x。

 
 

  同理区间[L,r]中有(toleft[dep][r]-toleft[dep][L-1])个数进去左子树,记它为y。
  所以,我们知道区间小区间[l,r]中有(y-x)个数进入左子树。那么如果(y-x)>=k,那么就在左子树中继续查找,否则就在右子树中继续查找。

 
 

  接着,解决查找的小区间的问题。
  如果接下来要查找的是左子树,那么小区间应该是[L+([L,l-1]区间进入左子树的个数),L+([L,r]区间内进入左子树的个数)-1]。即区间[L+x,L+y-1]。(这里的-1应该是防止边界问题)
    显然,这里k不用变。
  如果接下来要查找的是右子树,那么小区间应该是[mid+([L,l-1]区间中进入右子树的个数),mid+([L,r]区间进入右子树的个数)-1]。
    即区间[mid+(l-L-x),mid+(r-L-y)]。(这里要记得+1)
    显然,这里k要减去区间里已经进入左子树的个数,即k变为k-(y-x)。

 

#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int MAXSIZE = 100100;
int tree[30][MAXSIZE];  ///树最多也就30层
int _sort[MAXSIZE];
int toleft[30][MAXSIZE];

void build(int l,int r,int deep)
{
    if(l==r) return;
    int mid =(l+r)>>1;
    int same = mid-l+1; ///设这个变量的主要原因是判断tree[deep][i]==_sort[mid]时tree[deep][i]往哪边走
    for(int i=l; i<=r; i++)///计算放于左子树中与中位数相等的数字个数,
    {
       // printf("%d ",tree[deep][i]);
        if(tree[deep][i]<_sort[mid]) same--;
    }
    //printf("\n");
    int left = l;
    int right = mid+1;
    for(int i=l; i<=r; i++)
    {
        int flag = 0;
        if((tree[deep][i]<_sort[mid])||(tree[deep][i]==_sort[mid]&&same>0))
        {
            flag = 1;
            tree[deep+1][left++] = tree[deep][i];
            if(tree[deep][i]==_sort[mid]) same--;
        }
        else
        {
            tree[deep+1][right++] = tree[deep][i];
        }
        toleft[deep][i] = toleft[deep][i-1]+flag;
    }
    build(l,mid,deep+1);
    build(mid+1,r,deep+1);
}

int query(int L,int R,int l,int r,int k,int deep)
{
    if(l==r) return tree[deep][l];
    int x = toleft[deep][l-1]-toleft[deep][L-1];  ///x代表[L,l-1]区间内进入左子树的数字的量
    int y = toleft[deep][r]- toleft[deep][L-1]; ///y代表[L,r]区间内进入左子树的数字的量
    int cnt = y-x;  ///cnt代表[l,r]区间内进入左子树的数字的量
    int mid = (L+R)>>1;
    int rx = l - L - x;///l左边放在右子树中的数字个数
    int ry = r - L - y;///到r右边为止位于右子树的数字个数
    if(cnt>=k)
    {
        query(L,mid,L+x,L+y-1,k,deep+1);
    }else{
        query(mid+1,R,mid+rx+1,mid+ry+1,k-cnt,deep+1);
    }

}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {

        memset(toleft,0,sizeof(toleft));
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&_sort[i]);
            tree[0][i] = _sort[i];
        }

        sort(_sort+1,_sort+n+1);
        build(1,n,0);
        while(m--)
        {
            int a,b,k;
            scanf("%d%d%d",&a,&b,&k);
            int t = query(1,n,a,b,k,0);
            printf("%d\n",t);
        }
    }
    return 0;
}

 

 

你可能感兴趣的:(poj 2104(线段树))