POJ 2104 K-th Number (线段树)

K-th Number

Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 46589   Accepted: 15553
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 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.

题意:

给定一个数列a1,a2,…,an和m个三元组表示的查询。对于每个查询(i,j,k),输出ai,ai+1,…,aj的升序排列中第k个数。

分析:

使用线段树来解决这个问题。我们把数列用线段树维护起来。线段树的每个节点都保存了对应区间排好序的结果。在这之前我们接触过的线段树节点上保存的都是数值,而这次则有所不同,每个节点保存了一个数列。

建立线段树的过程和归并排序的类似,而每个节点的数列就是其两个儿子节点的数列合并后的结果。建树的复杂度是O(logn)。顺便一提,这棵树正是归并排序的完整再现。

要计算在某个区间中不超过x的数的个数,只需要递归地进行如下操作就可以了。

*如果所给的区间和当前节点的区间完全没有交集,那么返回0个。

*如果所给的区间完全包含了当前节点对应的区间,那么使用二分搜索法对该节点上保存的数组进行查找。

*否则对两个儿子递归地进行计算之后求和即可。

由于对应同一深度的节点最多只访问常数个,因此可以在O(log2n)时间里求出不超过x的数的个数。所以整个算法的复杂度是O(nlogn + mlog3n)。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#define maxn 100000
using namespace std;

vector<int> dat[4*maxn + 50];    //线段树的数据
int a[maxn + 50];
int n, q;

//构建线段树
//k是节点的编号,和区间[l, r)对应
void build(int k, int l, int r)
{
    if (r - l == 1) {
        dat[k].push_back(a[l]); return;
    }
    int lc = k << 1, rc = k << 1 | 1;
    build(lc, l, (l + r) / 2);
    build(rc, (l + r) / 2, r);
    dat[k].resize(r - l);
    //利用STL的merge函数把两个儿子的数列合并
    merge(dat[lc].begin(), dat[lc].end(), dat[rc].begin(), dat[rc].end(),dat[k].begin());
}

//计算[i, j)中不超过x的数的个数
//k是节点的编号,和区间[l, r)对应
int query(int i, int j, int x, int k, int l, int r)
{
    if (j <= l || r <= i)
        //完全不相交
        return 0;
    else if (i <= l&&r <= j){
        //完全包含在里面
        return upper_bound(dat[k].begin(), dat[k].end(), x) - dat[k].begin();
    }
    else {
        //对儿子递归地计算
        int lcnt = query(i, j, x, k << 1, l, (l + r) / 2);
        int rcnt = query(i, j, x, k << 1 | 1, (l + r) / 2, r);
        return lcnt + rcnt;
    }
}

int search(int x, int y, int k)
{
    int l = -1000000000 - 1;
    int r = -l + 2;
    while (l < r){
        int mid = (l + r) >> 1;
        int num = query(x, y+1, mid, 1, 1, n+1);
        if (k <= num) r = mid;
        else{
            l = mid + 1;
        }
    }
    return l;
}

int main()
{
    while (cin >> n >> q)
    {
        for (int i = 1; i <= n; i++){
            scanf("%d", a + i);
        }
        build(1, 1, n + 1);
        int li, ri, ki;
        for (int i = 0; i < q; i++){
            scanf("%d%d%d", &li, &ri, &ki);
            printf("%d\n", search(li, ri, ki));
        }
    }
    return 0;
}

时间: 2024-11-08 19:11:04

POJ 2104 K-th Number (线段树)的相关文章

【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 1436 Horizontally Visible Segments (线段树&amp;#183;区间染色)

题意   在坐标系中有n条平行于y轴的线段  当一条线段与还有一条线段之间能够连一条平行与x轴的线不与其他线段相交  就视为它们是可见的  问有多少组三条线段两两相互可见 先把全部线段存下来  并按x坐标排序  线段树记录相应区间从右往左当前可见的线段编号(1...n)  超过一条就为0  然后从左往右对每条线段  先查询左边哪些线段和它是可见的  把可见关系存到数组中  然后把这条线段相应区间的最右端可见编号更新为这条线段的编号  最后暴力统计有多少组即可了 #include <cstdio>

POJ 2155 Matrix (二维线段树)

http://poj.org/problem?id=2155 Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 18143   Accepted: 6813 Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. I

poj 2182 Lost Cows (线段树)

 Lost Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8838   Accepted: 5657 Description N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood 'wate

POJ 2777 Count Color (线段树区间更新加查询)

Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem. There is a very long board with length L centimeter, L is a positive integer, so we can evenly d

Minimum Inversion Number(线段树单点更新+逆序数)

Minimum Inversion Number(线段树单点更新+逆序数) Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy

poj 2528 Mayor&#39;s posters 线段树区间更新

Mayor's posters Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://poj.org/problem?id=2528 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at al

POJ 2528 Mayor&#39;s posters(线段树,区间覆盖,单点查询)

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 45703   Accepted: 13239 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

POJ训练计划2528_Mayor&#39;s posters(线段树/成段更新+离散化)

解题报告 地址传送门 题意: 一些海报,覆盖上去后还能看到几张. 思路: 第一道离散化的题. 离散化的意思就是区间压缩然后映射. 给你这么几个区间[1,300000],[3,5],[6,10],[4,9] 区间左右坐标排序完就是 1,3,4,5,6,9,10,300000; 1,2,3,4,5,6, 7 ,8; 我们可以把上面的区间映射成[1,8],[2,4],[5,7],[3,6]; 这样就节省了很多空间. 给线段染色, lz标记颜色. #include <map> #include <

POJ 2528 Mayor&#39;s posters (线段树区间更新+离散化)

题目链接:http://poj.org/problem?id=2528 给你n块木板,每块木板有起始和终点,按顺序放置,问最终能看到几块木板. 很明显的线段树区间更新问题,每次放置木板就更新区间里的值.由于l和r范围比较大,内存就不够了,所以就用离散化的技巧 比如将1 4化为1 2,范围缩小,但是不影响答案. 写了这题之后对区间更新的理解有点加深了,重点在覆盖的理解(更新左右两个孩子节点,然后值清空),还是要多做做题目. 1 #include <iostream> 2 #include <