POJ 2104 K-th Number 划分树

划分树(以下为转载)

  划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时间复杂度内)序列区间的第k大值。
  查找整序列的第k大值往往采用。然而此方法会破坏原序列,并且需要O(n)的时间复杂度。抑或使用二叉平衡树进行维护,此方法每次查找时间复杂度仅为O(logn)。然而此方法丢失了原序列的顺序信息,无法查找出某区间内的第k大值。
  划分树的基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数。查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,最后范围缩小到1,就找到了。
  划分树定义为,它的每一个节点保存区间[lft,rht]所有元素,元素顺序与原数组(输入)相同,但是,两个子树的元素为该节点所有元素排序后(rht-lft+1)/2个进入左子树,其余的到右子树,同时维护一个num域,num[i]表示lft->i这个点有多少个进入了左子树。
  

  如果由下而上看这个图,我们就会发现它和归并排序的(归并树)的过程很类似,或者说正好相反。归并树是由下而上的排序,而它确实是由上而下的排序,但这正是它可以用来解决第k大元素的理由所在。

 划分树的查找

  在区间[a,b]上查找第k大的元素,同时返回它的位置和区间小于[a,b]的所有数的和 。

  1.如果t[p].num[b]-t[p].num[a-1]>=k,即,进入左孩子的个数已经超过k个,那么就往左孩子里面查找,同时更新[a,b]=>[lft+t[p].num[a-1],lft+t[p].num[b]-1]

  2.如果t[p].num[b]-t[p].num[a-1]<k,即,进入p的左孩子的个数小于k个,那么就要往右孩子查找第k-s(s表示进入左孩子的个数)个元素。同时更新sum域,因而这样求出的sum只是严格小于在[a,b]区间中第k大的数的和。  

Language:
K-th Number
Time Limit: 20000MS Memory Limit: 65536K
Total Submissions: 58757 Accepted: 20391
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 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxm = 100005;
int tr[21][maxm], sl[21][maxm], n, m, a[maxm];
void build(int l, int r, int dep);
int query(int l, int r, int ql, int qr, int dep, int k);
int main()
{
	int i, j, k, sum, x, y;
	scanf("%d%d", &n, &m);
	for (i = 1;i <= n;i++)
	{
		scanf("%d", &tr[0][i]);
		a[i] = tr[0][i];
	}
	sort(a + 1, a + 1 + n);
	build(1, n, 0);
	while (m--)
	{
		scanf("%d%d%d", &x, &y, &k);
		printf("%d\n", query(1, n, x, y, 0, k));
	}
	return 0;
}
void build(int l, int r, int dep)
{
	if (l == r) return;
	int mid = (l + r) / 2, rev, idl, idr;
	rev = mid - l + 1;
	for (int i = l;i <= r;i++)
		if (tr[dep][i] < a[mid]) rev--;
	idl = l, idr = mid + 1;
	for (int i = l;i <= r;i++)
	{
		if (i == l) sl[dep][i] = 0;
		else sl[dep][i] = sl[dep][i - 1];
		if (tr[dep][i] < a[mid])
		{
			sl[dep][i]++;
			tr[dep + 1][idl++] = tr[dep][i];
		}
		else if (tr[dep][i] > a[mid])
			tr[dep + 1][idr++] = tr[dep][i];
		else
		{
			if (rev) { rev--, sl[dep][i]++, tr[dep + 1][idl++] = tr[dep][i]; }
			else tr[dep + 1][idr++] = tr[dep][i];
		}
	}
	build(l, mid, dep + 1);
	build(mid + 1, r, dep + 1);
}
int query(int l, int r, int ql, int qr, int dep, int k)
{
	int mid = (l + r) / 2, x1, x2, nl, nr;
	if (l == r) return tr[dep][l];
	if (l == ql) { x1 = 0, x2 = sl[dep][qr]; }
	else { x1 = sl[dep][ql - 1], x2 = sl[dep][qr] - x1; }
	if (x2 >= k)
	{
		nl = l + x1, nr = l + x1 + x2 - 1;
		return query(l, mid, nl, nr, dep + 1, k);
	}
	else
	{
		nl = mid - l + 1 + ql - x1;
		nr = mid - l + 1 + qr - x1 - x2;
		return query(mid + 1, r, nl, nr, dep + 1, k - x2);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值