K-th Number
Time Limit: 20000MS | Memory Limit: 65536K | |
Total Submissions: 55340 | Accepted: 19030 | |
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 31 5 2 6 3 7 42 5 34 4 11 7 3
Sample Output
563
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,m代表查询次数,对于每一次查询操作,会给出(i,j,k),即要你查询ai到aj之间从小到大排第k个数是多少,一共查询m次。
思路:挑战程序设计竞赛中的原题,按书上做法有两种,一种是桶分法,其目的是若给定一个数x,找到在区间[i,j]中小于等于x的元素个数,这样就可以进行二分搜索查找出需要查询的那个数。桶分法做法:即将元素b(b的大小自己设定)个一组放进不同的桶中,每个桶都已进行排序,查询的时候若查询区间完全覆盖一个桶,则可以二分搜索出这个桶中小于等于当前元素x的元素个数,若不完全覆盖,则一个一个元素拿出来比较,是否小于等于x。累加利用桶分法找到的所有小于等于x的元素个数,这样即可进行二分搜索找到合适的x。但桶分法的做法提交会TLE,所以还是利用线段树,利用线段树来找到所有小于等于当前元素x的值的个数会比较效率一些,线段树的每一个节点维护的是一个排好序的数列,递归查找即可,接下来的二分搜索找合适x的过程还是一样的。
AC代码:
#define _CRT_SECURE_NO_DEPRECATE#include#include #include using namespace std;const int N_MAX = 100000 + 4,M_MAX=5000+1;const int ST_SIZE = (1<<18)-1;int N, M;int A[N_MAX];int l[M_MAX], r[M_MAX], k[M_MAX];int nums[N_MAX];//nums是排好序的数列,对于其中每一个数进行二分搜索,判断是否符合要求vector dat[ST_SIZE];void init(int k,int l,int r) { //节点k,对应区间[l,r) if (r - l == 1) { dat[k].push_back(A[l]);//节点处的数列只有一个值 } else { int k1 = 2 * k + 1, k2 = 2 * k + 2; init(k1,l,(l+r)/2); init(k2,(l+r)/2,r); dat[k].resize(r-l);//先预置空间,才可以进行归并 merge(dat[k1].begin(), dat[k1].end(),dat[k2].begin(),dat[k2].end(),dat[k].begin()); }}int query(int a,int b,int x,int k,int l,int r) { //查找区间[a,b)中小于等于x的数的个数 if (l >= b || r <= a) { return 0; } else if (a <= l&&r <= b) { return upper_bound(dat[k].begin(),dat[k].end(),x)-dat[k].begin(); } else { int lc = query(a, b, x, 2 * k + 1, l, (l + r) / 2); int rc = query(a, b, x, 2 * k + 2, (l + r) / 2, r); return lc + rc; }}int main() { scanf("%d%d",&N,&M); for (int i = 0; i < N; i++) { scanf("%d",&A[i]); nums[i] = A[i]; } sort(nums,nums+N); for (int i = 0; i < M; i++) { scanf("%d%d%d",&l[i],&r[i],&k[i]); l[i]--, r[i]--; } init(0,0,N); for (int i = 0; i < M; i++) { int L = l[i], R = r[i]+1, K = k[i];//!!!!!+1别忘 int lb = -1, ub = N - 1; while (ub - lb > 1) { int mid = (lb + ub) >> 1; int kk = query(L, R, nums[mid], 0, 0, N); if (kk >= K)ub = mid; else lb = mid; } printf("%d\n",nums[ub]); } return 0;}