poj 2104 K-th Number - 经典划分树

原创 2015年07月08日 18:40:05

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 109 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

Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.


题目意思:给一个数组,问一个区间内第K大的数。

解题思路:划分树。划分树是基于快速排序的,首先将原始数组a[]进行排序sorted[],然后取中位数m,将未排序数组中小于m放在m左边,大于m的放在m右边,并记下原始数列中每个数左边有多少数小于m,用数组to_left[depth][]表示,这就是建树过程。重点在于查询过程,设[L,R]为要查询的区间,[l,r]为当前区间,s 表示[L,R]有多少数放到左子树,ss表示[l,L-1]有多少数被放倒左子树,如果s大于等于K,也就是说第K大的数肯定在左子树里,下一步就查询左子树,但这之前先要更新L,R,新的newl=l+ss, newr=newl+s-1。如果s小于k,也就是说第k大的数在右子树里,下一步查询右子树,也要先更新L,R,dd表示[l,L-1]中有多少数被放到右子树,d表示[L,R]有多少数被放到右子树,那么newl = m+1+dd,newr=m+d+dd, 这样查询逐渐缩小查询区间,直到最后L==R 返回最后结果就行。

给出一个大牛的图片例子,


代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
#define maxn 100000+100

int val[30][maxn];
int to_left[30][maxn];
int sorted[maxn];
int n;

void build(int l,int r,int d,int rt){
    if(l==r) return;
    int m = (l+r)>>1;
    int lsame = m-l+1;
    for(int i=l;i<=r;i++){
        if(val[d][i]<sorted[m]) lsame--;
    }
    int lpos=l,rpos=m+1,same=0;
    for(int i=l;i<=r;i++){
        if(i==l) to_left[d][i]=0;
        else to_left[d][i] = to_left[d][i-1];
        if(val[d][i]<sorted[m]){
            to_left[d][i]++;
            val[d+1][lpos++] = val[d][i];
        }else if(val[d][i]>sorted[m]){
            val[d+1][rpos++] = val[d][i];
        }else{
            if(same<lsame){
                same++;
                to_left[d][i]++;
                val[d+1][lpos++] = val[d][i];
            }else{
                val[d+1][rpos++] = val[d][i];
            }
        }
    }
    build(l,m,d+1,rt<<1);
    build(m+1,r,d+1,rt<<1|1);
}

void print(){
    printf("###\n");
    for(int i=0;i<10;i++){
        for(int j=1;j<=n;j++){
            cout << val[i][j]<<" ";
        }
        cout << endl;
    }
    printf("****\n");
    for(int i=0;i<10;i++){
        for(int j=1;j<=n;j++){
            cout << to_left[i][j]<<" ";
        }
        cout << endl;
    }
}

int query(int L,int R,int k,int l,int r,int d,int rt){
    if(L==R) return val[d][L];
    int s,ss;
    if(L==l){
        s = to_left[d][R];
        ss = 0;
    }else{
        s = to_left[d][R]-to_left[d][L-1];
        ss = to_left[d][L-1];
    }
    int m = (l+r)>>1;
    if(s>=k){
        int newl = l+ss;
        int newr = newl + s-1;
        return query(newl,newr,k,l,m,d+1,rt<<1);
    }else{
        int bb = (L-1)-l+1-ss;
        int b = R-L+1 -s;
        int newl = m+1+bb;
        int newr = m+bb+b;  // 4 5 1 3 2 (2,5,4)
        return query(newl,newr,k-s,m+1,r,d+1,rt<<1|1);
    }
}

int main(){
    int m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[0][i]);
        sorted[i]=val[0][i];
    }
    sort(sorted+1,sorted+n+1);
    build(1,n,0,1);
   // print();
    while(m--){
        int i,j,k;
        scanf("%d %d %d",&i,&j,&k);
        printf("%d\n",query(i,j,k,1,n,0,1));
    }
    return 0;
}


相关文章推荐

POJ 2104 K-th Number (线段树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 46589   Accepted: 15...

poj2104 k-th number 主席树入门讲解

定义:主席树是一种可持久化的线段树 又叫函数式线段树 刚开始学是不是觉得很蒙逼啊 其实我也是  主席树说简单了 就是保留你每一步操作完成之后的线段树 然后有可加减性 呃 。。。 这么说好...

POJ 2104 K-th Number (划分树,主席树写过了,这次是整体二分解法 )

还是先描述一下题意: 给出一个长度为n的数列,m次询问区间内的第k大数 对划分树,主席树和整体二分通过这题做了一下比较 划分树  1000ms+ 主席树 2000ms+ 整体二分 1500ms+ ...

poj 2104 K-th Number 主席树+超级详细解释

传送门:K-th Number题目大意:给出一段数列,让你求[L,R]区间内第几大的数字!在这里先介绍一下主席树! 如果想了解什么是主席树,就先要知道线段树,主席树就是n棵线段树,因为线段树只能维护...

可修改区间k-th number

可修改区间k-th number的介绍 不带修改的区间k-th number这个众所周知了。可以用主席树实现。 具体的话其实就是开nn棵权值线段树,动态开节点,由于每次增加一个点最多只会新建log2...

poj2104K-th Number (主席树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 46886   Accepted: 15...

用树解决K-th Number之主席树

K-th Number1.主席树解法前方高能,你需要(一些线段树姿势)答记者问Q:为什么叫主席树?叫主席树会不会给人一种钦定的感觉? A:因为某大佬考场上忘记怎么写划分树了,于是当场yy出来了,叫主...

POJ 2104 K-th Number(区间第k大数)(平方分割,归并树,划分树)

题目链接: http://poj.org/problem?id=2104 解题思路: 因为查询的个数m很大,朴素的求法无法在规定时间内求解。因此应该选用合理的方式维护数据来做到高效地查询。 如果x是...

划分树 K-th Number

K-th Number Time Limit:20000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit S...

扩展kmp详解

转自http://blog.csdn.net/dyx404514/article/details/41831947 算法总结第二弹,上次总结了下kmp,这次就来拓展kmp吧。 拓展kmp是对...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:poj 2104 K-th Number - 经典划分树
举报原因:
原因补充:

(最多只允许输入30个字)