题目链接

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

题意:

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

分析:

因为查询的个数m很大,朴素的求法没法在规定的时间内求出。

如果x是第k个数,那么一定有:

1.在区间中不超过x的数不少于k个

2.在区间中小于x的数不到k个

因此,如果可以快速求出区间里不超过x的数的个数,就可以通过对x进行二分搜索求出第k个数是多少。

接下来,我们来看一下如何计算在某个区间里不超过x的数的个数。如果不进行预处理,那么就只能遍历一遍所有的元素。

另一方面,如果区间是有序的,那么就可以通过二分搜索法高效地求出不超过x的数的个数了。但是,如果对于每个查询都分别进行一次排序,就完全无法降低复杂度。所以,可以考虑使用平方分割和线段树进行求解。

首先看一下如何使用平方分割来解决这个问题。把数列每b个一组分到哥哥桶里,每个桶内保存有序后的序列。这样,如果要求在某个区间中不超过x的个数,就可以这样求得:

1.对于完全包含在区间内的桶,用二分搜索法进行计算

2.对于所在的桶不完全包含在区间内的元素,逐个检查。

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<string.h>
  4. #include<vector>
  5. #include<algorithm>
  6. using namespace std;
  7. typedef long long ll;
  8. const int B = 1072;
  9. const int maxn = 1e5+5;
  10. int n, m;
  11. int a[maxn], num[maxn];///原序列和排序后的序列
  12. vector<int> buk[maxn/B+1];///每个桶排序后的结果
  13. int main()
  14. {
  15. scanf("%d%d",&n,&m);
  16. for(int i = 0; i < n; i++)
  17. {
  18. scanf("%d", a+i);
  19. buk[i/B].push_back(a[i]);
  20. }
  21. memcpy(num,a,sizeof(int)*n);
  22. sort(num,num+n);
  23. ///(n-1)/b <= n/b 当 n%b != 0时候取等号,这时候不是一个完整的桶,所以最后一个桶不sort也没关系
  24. for(int i = n/B;i>=0; i--) sort(buk[i].begin(),buk[i].end());
  25. while(m--)
  26. {
  27. int l,r,k;
  28. scanf("%d%d%d",&l,&r,&k);
  29. int lb = 0, ub = n-1;
  30. while(lb < ub)
  31. {
  32. int md = (lb+ub)>>1;
  33. int x = num[md];
  34. ///求[l,r)区间中第k个数
  35. int p = l-1, q = r, c = 0;
  36. ///区间两端多出的部分
  37. while(p<q && p%B) if(a[p++] <= x) c++;
  38. while(p<q && q%B) if(a[--q] <= x) c++;
  39. ///对每一个桶进行计算
  40. for(int i = p/B, mi = q/B; i < mi; i++)
  41. {
  42. c += upper_bound(buk[i].begin(),buk[i].end(),x)-buk[i].begin();
  43. }
  44. c >= k?ub = md: lb = md+1;
  45. }
  46. printf("%d\n", num[lb]);
  47. }
  48. return 0;
  49. }

思路同上,只是计算cnt的时候改用线段树。线段树每个结点保存有序的数组,建树过程是归并排序的完整再现,因此也叫归并树。

更抽象来看,数值大小和位置是两个维度,查询就是询问一个空间内的点数,也就是所谓的区域树了,通过嵌套可以推广到高维。

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<string>
  4. #include<cstring>
  5. #include<queue>
  6. #include<vector>
  7. #include<stack>
  8. #include<vector>
  9. #include<map>
  10. #include<set>
  11. #include<algorithm>
  12. #include<cmath>
  13. using namespace std;
  14. typedef long long ll;
  15. #define para int d = 0, int l = 0,int r = n
  16. #define lsn d+1, l, mid
  17. #define rsn d+1, mid, r
  18. #define insd ql<=l&&r<=qr
  19. const int maxn = 1e5;
  20. int n, m;
  21. int a[maxn];
  22. const int Log2N_ = 18;
  23. int dat[Log2N_][maxn];
  24. void build(para)
  25. {
  26. if(r-l == 1)
  27. {
  28. dat[d][l] = a[l];
  29. }
  30. else
  31. {
  32. int mid = (l+r)>>1;
  33. build(lsn);
  34. build(rsn);
  35. merge(dat[d+1]+l,dat[d+1]+mid,dat[d+1]+mid,dat[d+1]+r,dat[d]+l);
  36. }
  37. }
  38. int ql, qr, qval;
  39. int query(para)
  40. {
  41. if(insd)
  42. {
  43. return upper_bound(dat[d]+l,dat[d]+r,qval) - dat[d]-l;
  44. }
  45. else
  46. {
  47. int res = 0;
  48. int mid = (l+r)>>1;
  49. if(ql<mid) res += query(lsn);
  50. if(qr>mid) res += query(rsn);
  51. return res;
  52. }
  53. }
  54. int main()
  55. {
  56. scanf("%d%d",&n,&m);
  57. for(int i = 0; i < n; i++)
  58. {
  59. scanf("%d", a+i);
  60. }
  61. build();
  62. while(m--)
  63. {
  64. int k;
  65. scanf("%d%d%d",&ql,&qr,&k);
  66. ql--;
  67. int lb = 0, ub = n-1;
  68. while(lb < ub)
  69. {
  70. int md = (lb+ub)>>1;
  71. qval = dat[0][md];
  72. query() >= k? ub = md:lb = md+1;
  73. }
  74. printf("%d\n", dat[0][lb]);
  75. }
  76. return 0;
  77. }

POj 2104 K-th Number (分桶法+线段树)的更多相关文章

  1. POJ 2104 K-th Number(分桶,线段树,主席树)

    一道比较经典的数据结构题.可以用多种方式来做. 一,分桶法(平方分解). 根据数字x的大小和区间内不大于x的数字数量cnt的单调性,可知第k大数kth对应的cnt应该满足cnt≥k, 且kth是满足条 ...

  2. 静态区间第k大(分桶法和平方分割)

    POJ 2104为例 思想: <挑战程序设计竞赛>中介绍的方法. 分桶法:把一排物品或者平面分成桶,每个桶分别维护自己内部的信息,已达到高效计算的目的. 设一共有n个数,每b个分到一个桶里 ...

  3. LeetCode 220. Contains Duplicate III (分桶法)

    Given an array of integers, find out whether there are two distinct indices i and j in the array suc ...

  4. 【POJ 2482】 Stars in Your Window(线段树+离散化+扫描线)

    [POJ 2482] Stars in Your Window(线段树+离散化+扫描线) Time Limit: 1000MS   Memory Limit: 65536K Total Submiss ...

  5. poj 3468:A Simple Problem with Integers(线段树,区间修改求和)

    A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 58269   ...

  6. POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询)

    POJ.3468 A Simple Problem with Integers(线段树 区间更新 区间查询) 题意分析 注意一下懒惰标记,数据部分和更新时的数字都要是long long ,别的没什么大 ...

  7. POJ 2104 K-th Number (可持久化线段树)

    题目大意 给一个长度为n的序列,有m个询问,每次询问一个区间里面第k小的数. 解题分析 静态的区间第k大.复习了一下可持久化线段树. 首先对数值离散化,建一颗权值线段树.按照序列的顺序依次插入,每一个 ...

  8. poj 3468 A Simple Problem with Integers(线段树、延迟更新)

    A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 74705   ...

  9. poj 3468 A Simple Problem with Integers(线段树+区间更新+区间求和)

    题目链接:id=3468http://">http://poj.org/problem? id=3468 A Simple Problem with Integers Time Lim ...

随机推荐

  1. lintcode-142-O(1)时间检测2的幂次

    142-O(1)时间检测2的幂次 用 O(1) 时间检测整数 n 是否是 2 的幂次. 样例 n=4,返回 true; n=5,返回 false. 挑战 O(1) time 标签 比特位操作 思路 使 ...

  2. LintCode-159.寻找旋转排序数组中的最小值

    寻找旋转排序数组中的最小值 假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2). 你需要找到其中最小的元素. 你可以假设数组中不存在重复的 ...

  3. java中sql语句能不能加分号的问题?

    一.原因  在程序运行中,当执行sql后总是报无效字符错误:但是把程序放在pl/sql中执行又没有错误.让我很纳闷!于是我开始查找资料,然后我终于发现了问题. 二.问题剖析 原来在程序中:如果你在程序 ...

  4. 【bzoj5108】[CodePlus2017]可做题 拆位+乱搞

    题目描述 给出一个长度为 $m$ 的序列 $a$ ,编号为 $a_1\sim a_m$,其中 $n$ 个位置的数已经确定,剩下的位置的数可以任意指定.现在令 $b$ 表示 $a$ 的前缀异或和,求 $ ...

  5. 【bzoj4326】[NOIP2015]运输计划 二分答案+LCA

    题目描述 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该 ...

  6. 使用thymeleaf实现div中加载html

    目标:固定顶部或者左侧导航,点击导航动态更新中间content区域的页面,也就是在放一个div在页面上,把html加载到div里,以前类似的实现都是通过Iframe或者js实现,在使用springbo ...

  7. 【刷题】BZOJ 1002 [FJOI2007]轮状病毒

    Description 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个N轮状基由圆环上N个不同的基原子 和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道.如下 ...

  8. [洛谷P5057][CQOI2006]简单题

    题目大意:有一个长度为$n$的$01$串,两个操作: $1\;l\;r:$把区间$[l,r]$翻转($0->1,1->0$) $2\;p:$求第$p$位是什么 题解:维护前缀异或和,树状数 ...

  9. POJ1422:Air Raid——题解

    http://poj.org/problem?id=1422 题目大意:n个点m条有向边,每条边只能走一次,往点上放人让他们走遍所有边,问至少要多少人. —————————————————————— ...

  10. BZOJ3571 & 洛谷3236:[HNOI2014]画框——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3571 https://www.luogu.org/problemnew/show/P3236 小T ...