划分树 poj2104 hdu5249

本文解析了两个典型的数据结构与算法问题:KPI计算问题与K-thNumber查询问题。通过介绍问题背景、输入输出格式及示例,重点讨论了如何利用划分树解决子区间第k大数的问题,提供了核心代码实现。

KPI

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 616    Accepted Submission(s): 261


Problem Description
你工作以后, KPI 就是你的全部了. 我开发了一个服务,取得了很大的知名度。数十亿的请求被推到一个大管道后同时服务从管头拉取请求。让我们来定义每个请求都有一个重要值。我的KPI是由当前管道内请求的重要值的中间值来计算。现在给你服务记录,有时我想知道当前管道内请求的重要值得中间值。
 

Input
有大约100组数据。

每组数据第一行有一个n(1n10000),代表服务记录数。

接下来有n行,每一行有3种形式
  "in x": 代表重要值为x(0x109)的请求被推进管道。
  "out": 代表服务拉取了管道头部的请求。
  "query: 代表我想知道当前管道内请求重要值的中间值. 那就是说,如果当前管道内有m条请求, 我想知道,升序排序后第floor(m/2)+1th 条请求的重要值.

为了让题目简单,所有的x都不同,并且如果管道内没有值,就不会有"out"和"query"操作。
 

Output
对于每组数据,先输出一行

Case #i:
然后每一次"query",输出当前管道内重要值的中间值。
 

Sample Input
6 in 874 query out in 24622 in 12194 query
 

Sample Output
Case #1: 874 24622
 
K-th Number
Time Limit: 20000MS Memory Limit: 65536K
Total Submissions: 41138 Accepted: 13447
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.

这两题都 要求子区间的第k大数,如果用快排之后,再查,复杂度太大,不能过,就要用到划分树了,划分树,其实,就是线段树的

一个变种了,当然,这样做查询是lg(n)级别,建树是n * lg(n),再加上一次快排,也是n * lg(n),差不多就可以过了!

先来看看划分树,我们在线段树的基础上,如果每个结点都保存了,当前子段向左子树走的个数,如果,查询的k要小于,查询段向左走的个数,自然,我们向左子树就可以查到结果,如果,k>向左走的个数,当然要向右找k-midcount个数,区间的变换,我们可以

推一下向左走就是l + scount, l + ecount - 1,向右走就是mid + 1 + s - l - scount, mid + 1 + e - l - ecount,其中scount就是s之前向左走的个数, ecount,就是e之前向左走的个数。

上核心代码

#define MID(a,b) (((a)+(b))>>1)
#define N 100050
int num[20][N], val[20][N];
//第i层,向左包括自已,向左子树的个数,当前的值
int pri[N], sorted[N];
//原始和排序后的数列
char str[20];
void build(int l, int r, int layer){
	if (l >= r){
		return;
	}
	int mid = MID(l, r);
	int ql = l, qr = mid + 1, leftCount = mid, eqCount = 0;
	for (int i = l; i <= r; i++)
		leftCount -= val[layer][i] < sorted[mid];
	for (int i = l; i <= r; i++){
		if (i == l){
			num[layer][i] = 0;
		}
		else{
			num[layer][i] = num[layer][i - 1];
		}
		if (val[layer][i] < sorted[mid]){
			num[layer][i]++;
			val[layer + 1][ql++] = val[layer][i];
		}
		else if (val[layer][i] > sorted[mid]){
			val[layer + 1][qr++] = val[layer][i];
		}
		else {
			if (eqCount < leftCount){
				eqCount++;
				num[layer][i]++;
				val[layer + 1][ql++] = val[layer][i];
			}
			else {
				val[layer + 1][qr++] = val[layer][i];
			}
		}
	}
	build(l, mid, layer + 1);
	build(mid + 1, r, layer + 1);
}
//在layer层l到r间,找s-e之间的第k大数
int query(int l, int r, int layer, int s, int e, int k){
	if (l >= r || s >= e){
		return val[layer][s];
	}
	int scount, ecount, mid = MID(l, r);
	if (s == l){
		scount = 0, ecount = num[layer][e];
	}
	else {
		scount = num[layer][s - 1], ecount = num[layer][e];
	}
	int midcount = ecount - scount;
	if (k <= midcount){
		return query(l, mid, layer + 1, l + scount, l + ecount - 1, k);
	}
	else {
		return query(mid + 1, r, layer + 1, mid + 1 + s - l - scount, mid + 1 + e - l - ecount, k - midcount);
	}
}
int main()
{
	int tcase, tcasenum = 0;
	int n, k, q, s, e, begin = 1, nn, qcount, m;
	while (S2(n, m) != EOF)
	{
		FI(n){
			S(val[0][i+1]);
		}
		FI(n){
			sorted[i + 1] = val[0][i + 1];
		}
		sort(sorted + 1, sorted + n + 1);
		build(1, n, 0);
		FI(m){
			S2(s, e); S(q);
			Prn(query(1, n, 0, s,e,q));
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值