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

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;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 09:41:50

poj 2104 K-th Number - 经典划分树的相关文章

POJ 2104:K-th Number(主席树静态区间k大)

题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var t:array[0..100000*50]of point; a,b,id,root:array[0..100000]of longint; n,i,m,x,y,k,v,len:longint; procedure qsort(l,h:longint); var i,j,t,m:longint; b

POJ 题目2140 K-th Number(划分树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 42155   Accepted: 13855 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

hdu 2665 Kth number(划分树)

Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4602 Accepted Submission(s): 1468 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first l

poj 2104:K-th Number(划分树,经典题)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 35653   Accepted: 11382 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

POJ 2104 K-th Number (划分树)

                                                            K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 52651   Accepted: 18091 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures

POJ--2104--K-th Number【划分树模板】

链接:http://poj.org/problem?id=2104 题意:给一个n个元素的数组,m次询问,每次查询区间[i,j]中第k大的数,输出这个数. 思路:划分树模板题 划分树讲解:传送门 我用的是kuangbin大神的模板 #include<cstring> #include<string> #include<fstream> #include<iostream> #include<iomanip> #include<cstdio&

POJ 2104 K-th Number(划分树)

题目链接:http://poj.org/problem?id=2104 #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int N = 100007; int n, m; int sorted[N], tree[20][N], toleft[20][N]; void build(int L, int R, int dep) { if (L ==

【POJ 2104】 K-th Number 主席树模板题

达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没有评测,但我立下flag这个代码一定能A.我的同学在自习课上考语文,然而机房党都跑到机房来避难了\(^o^)/~ #include<cstdio> #include<cstring> #include<algorithm> #define for1(i,a,n) for(i

POJ 2104:K-th Number(整体二分)

http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二分能够解决的询问,如果有多个,那么如果支持离线处理的话,那么就可以使用整体二分了. 在这题二分可行的答案,根据这个答案,把询问操作丢在左右两个队列里面分别递归继续按这样处理.注释里写的很详细. 1 #include <iostream> 2 #include <cstdlib> 3 #