南邮 OJ 1406 第K小的数

第K小的数

时间限制(普通/Java) :  1000 MS/ 3000 MS          运行内存限制 : 65536 KByte
总提交 : 163            测试通过 : 33 

比赛描述

你为SKZ公司的数据结构部门工作,你的工作是重新写一个程序,这个程序能快速地找到一段数列中第k小的数。

就是说,给定一个整数数列a[1..n],其中每个元素都不相同,你的程序要能回答一组格式为Q (i , j , k)的查询,Q(i, j ,k)的意思是“在a[i..j]中第k小的数是多少?”

例如令 a = {1, 5, 2, 6, 3, 7, 4},查询格式为Q (2 , 5 , 3),数列段a[2..5] = {5, 2, 6, 3},第3小的数是5,所以答案是5。



输入

第一行包括一个正整数n,代表数列的总长度,还有一个数m,代表有m个查询。n、m满足:1≤n≤100 000,1≤m≤5 000。

第二行有n个数,代表数列的元素,所有数都不相同,而且不会超过109 。

接下来有m行,每行三个整数i、j、k,代表一次查询,i、j、k满足:1≤i≤j≤n, 1≤k≤j−i+1。

输出

输出每个查询的答案,用换行符隔开。

样例输入

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

样例输出

5
6
3

提示

 

题目来源

JSOI2010




/* 大根堆 Time Limit Exceed at Test 2
#include<iostream>
#define MAX_N 100000

int a[MAX_N];
int h[MAX_N];

void maxHeapAdjust(int *a,int i,int n){
	int c = (i<<1)|1;
	while(c<n){
		if(c+1<n && a[c+1]>a[c]){
			c++;
		}
		if(a[i]<a[c]){
			a[i] ^= a[c];
			a[c] ^= a[i];
			a[i] ^= a[c];
			i = c;
		}else{
			return;
		}
	}
}

int main(){
	int n,m,i,j,k,p;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++){
		scanf("%d",a+i);
	}
	while(m--){
		scanf("%d%d%d",&i,&j,&k);
		i--;
		j--;
		for(p=0;p<k;p++){
			h[p] = INT_MAX;
		}
		while(i<=j){
			if(a[i]<h[0]){
				h[0] = a[i];
				maxHeapAdjust(h,0,k);
			}
			i++;
		}
		printf("%d\n",h[0]);
	}
}
*/

/* 伴随数组 Time Limit Exceed at Test 2
#include<iostream>
#include<algorithm>
#define MAX_N 100000

struct num{
	int val,index;
};

bool operator<(const num n1,const num n2){
	return n1.val<n2.val;
}

num a[MAX_N];

int main(){
	int n,m,i,j,k,l;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++){
		scanf("%d",&a[i].val);
		a[i].index = i;
	}
	std::sort(a,a+n);
	while(m--){
		scanf("%d%d%d",&i,&j,&k);
		i--;
		j--;
		for(l=0;k;){
			if(a[l].index>=i && a[l].index<=j){
				if(--k==0){
					break;
				}
			}
			l++;
		}
		printf("%d\n",a[l]);
	}
}
*/


/* 剪枝后的快排 Time Limit Exceed at Test 2
#include<iostream>
#define MAX_N 100000

int a[MAX_N];
int b[MAX_N];

// 在c[0]至c[len-1]中,查找第k小的数
int select(int *c,int len,int k){
	int i=0,j=len-1,temp=c[0];
	while(i<j){
		while(i<j && c[j]>=temp){
			j--;
		}
		c[i] = c[j];
		while(i<j && c[i]<=temp){
			i++;
		}
		c[j] = c[i];
	}
	c[i] = temp;
	if(i==k-1){
		return c[i];
	}else if(i<k-1){
		return select(c+i+1,len-i-1,k-i-1);
	}else{
		return select(c,i,k);
	}
}

int main(){
	freopen("test.txt","r",stdin);
	int n,m,i,j,k,p,q;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++){
		scanf("%d",a+i);
	}
	while(m--){
		scanf("%d%d%d",&i,&j,&k);
		i--;
		j--;
		for(p=0,q=i;q<=j;){
			b[p++] = a[q++];
		}
		printf("%d\n",select(b,j-i+1,k));
	}
}
*/





/*
// 129MS Internet
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 100001;
int sorted[MAXN];   //对原集合中元素排序后的值
int val[30][MAXN];  //val记录第k层当前位置的值
int toleft[30][MAXN];  //记录元素所在区间当前位置前的元素进入到左子树的个数
int sum[30][MAXN];  //记录比当前元素小的元素的和
int n;

void build(int l, int r, int d) {
    if (l == r) return ;
    int mid = (l + r) >> 1;
    int same = mid - l + 1;
    for (int i=l; i<=r; i++)
        if (val[d][i] < sorted[mid])
            same--;
    int lp = l, rp = mid+1;
    for (int i=l; i<=r; i++) {
        if (i == l) toleft[d][i] = 0;
        else toleft[d][i] = toleft[d][i-1];

        if (val[d][i] < sorted[mid]) {
            toleft[d][i]++;
            val[d+1][lp++] = val[d][i];
        } else if (val[d][i] > sorted[mid])
            val[d+1][rp++] = val[d][i];
        else {
            if (same) {
                same--;
                toleft[d][i]++;
                val[d+1][lp++] = val[d][i];
            } else val[d+1][rp++] = val[d][i];
        }
    }
    build(l, mid, d+1);
    build(mid+1, r, d+1);
}
int query(int a, int b, int k, int l, int r, int d) {
    if (a == b) return val[d][a];

    int mid = (l + r) >> 1;
    int s, ss, sss;
    if (a == l) {
        s = toleft[d][b];
        ss = 0;
    } else {
        s = toleft[d][b] - toleft[d][a-1];
        ss = toleft[d][a-1];
    }

    if (s >= k) {
        a = l + ss;
        b = l + ss + s - 1;
        return query(a, b, k, l, mid, d+1);
    } else {
        a = mid+1 + a - l - ss;
        b = mid+1 + b - l - toleft[d][b];
        return query(a, b, k-s, mid+1, r, d+1);
    }
}
int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i=1; i<=n; i++) {
        scanf("%d", &sorted[i]);
        val[0][i] = sorted[i];
    }
    sort(sorted+1, sorted+1+n);
    build(1, n, 0);

    int a, b, k;
    while (m--) {
        scanf("%d%d%d", &a, &b, &k);
        printf("%d\n", query(a, b, k, 1, n, 0));
    }
    return 0;
}
*/

#include <iostream> 
#include <cstdio> 
#include <algorithm> 
using namespace std; 

#define N 100500 
#define MID ((l+r)>>1)

int a[N];					//原数组
int s[N];					//排序好之后的数组
int t[20][N];				//划分树第 k 层
int num[20][N];				//对于第i个数,记录在[l,i]区间内有多少数被划入左子树
int n,m;

// 建树,第 c 层的 l 到 r
void Build(int c,int l,int r){
	int mid=(l+r)>>1;
    int lm=mid-l+1;			//左端点到中点有多少元素,处理多个值等于s[mid]
	int lp=l;				//左子树的第一个元素位置
	int rp=mid+1;			//右子树的第一个元素位置
	int i;
	for(i=l;i<=mid;i++){
		lm-=s[i]<s[mid];	//记录在[l,mid]中有多少元素等于s[mid],即左子树应该存放多少个s[mid]
	}
    for(i=l;i<=r;i++){
		if( i==l ){
			num[c][i]=0;
		}else{
			num[c][i]=num[c][i-1];	// ? 对于第i个数,记录在[l,i]区间内有多少数被划入左子树
		}
		if( t[c][i]==s[mid] ){ 
            if( lm ){
                lm--; 
                num[c][i]++; 
                t[c+1][lp++]=t[c][i]; 
			}else{
                t[c+1][rp++]=t[c][i];
			}
        }else if( t[c][i]<s[mid] ){ 
            num[c][i]++;
            t[c+1][lp++]=t[c][i]; 
		} else{
            t[c+1][rp++]=t[c][i];
		}
    } 
    if( l<r ) 
        Build(c+1,l,mid),Build(c+1,mid+1,r); 
} 
//c:t[][]数组的层次
//l:左端点
//r:右端点
//ql:查询区间的左端点
//qr:查询区间的右端点
//k:第 k 小的数
//功能:在 t[c][] 中的[l,r]区间的子区间[ql,qr]中,查找第 k 小的数
int Query(int c,int l,int r,int ql,int qr,int k) 
{
    if( l==r ) 
        return t[c][l]; 
    int s,ss; 
    if( l==ql ) 
        s=0,ss=num[c][qr];								// s:在ql的左边有多少个元素在左子树
    else
        s=num[c][ql-1],ss=num[c][qr]-num[c][ql-1];		//ss:[ql,qr]中有多少个在左子树
    if( k<=ss )
        return Query(c+1,l,MID,l+s,l+s+ss-1,k); 
    else
        return Query(c+1,MID+1,r,MID+1+ql-l-s,MID+1+qr-l-s-ss,k-ss); 
}
   
int main() { 
    freopen("test.txt","r",stdin);
	scanf("%d%d",&n,&m); 
    for(int i=1;i<=n;i++) { 
        scanf("%d",&a[i]); 
        s[i]=t[0][i]=a[i]; 
    }
    sort(s+1,s+1+n); 
    Build(0,1,n); 
    while( m-- ) 
    { 
        int l,r,k; 
        scanf("%d%d%d",&l,&r,&k); 
        printf("%d\n",Query(0,1,n,l,r,k)); 
    } 
    return 0; 
}





你可能感兴趣的:(ACM,南邮OJ,第K小的数)