PKU 2104 K-th Number

题目:http://poj.org/problem?id=2104

借此题学习了一下划分树

划分树定义: 他的每个节点保存【l, r】区间的所有元素,元素的排列顺序与输入的顺序相同,但是其两个子树的元素是所有元素排序后, 
有(l+r)/2个元素进入左子树,其余的进入右子树,同时  维护一个num[]变量, num[i]表示l->i中, 有多少个元素进入了左子树。


红色代表进入左子树的元素。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn=100000;
struct TreeNode
{
	int var[maxn+10];
	int num[maxn+10];
}tn[30];
int sorted[maxn+10];
int n, m, a, b, c;
void BuildTree(int v, int left, int right)
{
	if (left==right) return;
	int mid=(left+right)>>1;
	int lchild=left, rchild=mid+1;
	int isame=mid-left+1, same=0;
	for (int i=left; i<=right; i++) if (tn[v].var[i]<sorted[mid]) isame--;
	for (int i=left; i<=right; i++)
	{
		if (i==left) tn[v].num[i]=0;
		else tn[v].num[i]=tn[v].num[i-1];
		if (tn[v].var[i]<sorted[mid])
		{
			tn[v].num[i]++;
			tn[v+1].var[lchild++]=tn[v].var[i];
		}
		else if (tn[v].var[i]>sorted[mid]) tn[v+1].var[rchild++]=tn[v].var[i];
		else
		{
			if (same<isame)
			{
				same++;
				tn[v].num[i]++;
				tn[v+1].var[lchild++]=tn[v].var[i];
			}
			else tn[v+1].var[rchild++]=tn[v].var[i];
		}
	}
	BuildTree(v+1, left, mid);
	BuildTree(v+1, mid+1, right);
}
//在区间【l, r】中查找第K大元素
//1.如果tn[v].num[r]-tn[v].num[l-1]>=k,也就是说在区间【l, r】中已经有超过k个元素进入到左子树了,并且其顺序不变,所以
//在其左子树中查找,并且其区间变成【left+tn[v].num[l-1], left+tn[v].num[r]-1】
//2.如果tn[v].num[r]-tn[v].num[l-1]<k,也就是说在区间【l, r】中少于K个元素进入左子树,那么就要在右子树中查找第k-s(s代表进入左子树的元素个数)个元素。
//其区间变为【mid+【left, l-1】中分到右子树中元素的个数+1, mid+【l, r】中分到右子树中元素的个数】。
int Query(int l, int r, int k, int v, int left, int right)
{
	if (left==right) return tn[v].var[l];
	int mid=(left+right)>>1;
	int s, ss, b, bb;
	if (l==left)
	{
		s=tn[v].num[r];
		ss=0;
	}
	else
	{
		s=tn[v].num[r]-tn[v].num[l-1];
		ss=tn[v].num[l-1];
	}
	if (s>=k) 
	{
		l=left+ss;
		r=left+ss+s-1;
		return Query(l, r, k, v+1, left, mid);
	}
	else
	{
		b=l-left-ss;
		bb=r-l+1-s;
		l=mid+b+1;
		r=mid+b+bb;
		return Query(l, r,  k-s, v+1, mid+1, right);
	}
}
int main()
{
	//freopen("in.txt", "r", stdin);
	while (scanf("%d %d", &n, &m)==2)
	{
		for (int i=1; i<=n; i++)
		{
			scanf("%d", &sorted[i]);
			tn[1].var[i]=sorted[i];
		}
		sort(sorted+1, sorted+n+1);
		BuildTree(1, 1, n);
		for (int i=0; i<m; i++)
		{
			scanf("%d %d %d", &a, &b, &c);
			printf("%d\n", Query(a, b, c, 1, 1, n));
		}
	}
	return 0;
}











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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值