来自《挑战程序设计竞赛》
1.题目原文
K-th Number
Time Limit: 20000MS | Memory Limit: 65536K | |
Total Submissions: 50565 | Accepted: 17253 | |
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.
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).
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 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.
Source
Northeastern Europe 2004, Northern Subregion
2.解题思路1:线段树
线段树中每个点维护的是对应区间排好序后的序列。建树的过程和归并排序很像,每个结点的数列就是两个儿子结点的数列合并后的结果,建树的时间复杂度是O(nlogn)。这样的线段树也叫归并树。
要计算第k大值,可以利用二分法,查询区间中比x小的数有多少个。
要计算在某个区间中不超过x的数的个数,只要递归进行就可以了。
1.所给区间与当前区间完全没有交集,返回0
2.所给的区间完全包含了当前区间,使用二分搜索法对该结点上保存的数组进行查找
3.否则对两个儿子进行递归计算即可。
由于同一深度的结点最多只访问常数个,因此可以在O(log2n)时间内求出不超过x的数的个数。整个算法的时间复杂度是O(nlogn+m(logn)^3)。
3.AC代码
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#include<cmath>
#include<bitset>
#include<sstream>
#include<stack>
using namespace std;
#define INF 0x7fffffff
typedef long long ll;
const int maxn=100000+10;
const int ST_SIZE=(1<<18)-1;
//编号从0到n-1
int n,m;
int A[maxn];
int nums[maxn];//对A排序之后的结果
vector<int> dat[ST_SIZE];
//构造线段树
//k是结点的编号,与区间[l,r)
void init(int k,int l,int r)
{
if(r-l==1){
dat[k].push_back(A[l]);
}
else{
int lch=2*k+1,rch=2*k+2;
init(lch,l,(l+r)/2);
init(rch,(l+r)/2,r);
dat[k].resize(r-l);
//利用STL中的merge函数把两个儿子的数列合并
merge(dat[lch].begin(),dat[lch].end(),dat[rch].begin(),dat[rch].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 lc=query(i,j,x,2*k+1,l,(l+r)/2);
int rc=query(i,j,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);
init(0,0,n);
while(m--){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
l--;
int lb=-1,ub=n-1;
while(ub-lb>1){
int md=(lb+ub)/2;
int c=query(l,r,nums[md],0,0,n);
if(c>=k) ub=md;
else lb=md;
}
printf("%d\n",nums[ub]);
}
return 0;
}