杭电ACM2665——Kth number~~划分树

题目的意思:给点区间[a, b],查找第K大的数,和POJ2104题一样,只是HDU上的时间限制5000MS,用我在POJ上的方法,过不了,会超时。

而这一题的代码,改一下main函数的输入,就可以直接AC了POJ上的2104.

这题,用分桶法,WR,纠结了一晚上,最后还是放弃了,实在不知道错在哪里。于是改用了划分树的方法,学习了划分树的建立和查找。

划分树:主要运用于求解序列中区间[a, b]上的第K大的数,也就是区间[a, b]从小到大排序,第K个。

主要的算法思路是:

1,建树:先排序好存放在一个数组中,然后找到中间那个数,在原来数组中将小于中间值的数放在左边,大于的数放在右边。按照这样建立一颗树。记录各个点进入左子树的个数,在后面查找会用到。

如图:图是盗用的,没时间自己做。

2,查找:查找区间[a, b]上的第K个数。如果进入左孩子的数的个数大于等于K,进入左子树进行查找,如果小于,则进入右子树查找,查找第k - s大的数,s为区间[a,b]进入左子树的数的个数。更新区间[a,b]。

代码可以当作模版来用。

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

const int M = 100010;

class node
{
public:
	int num[M];    //该位置进入左孩子的个数
	int val[M];    //当前位置的值
}t[30];
int sorted[M];

void build(int left, int right, int p)
{
	if(left == right)
		return;
	int i, mid = (left + right) / 2;
	int isame = mid - left + 1;
	/*
	isame用来判断是否存在与sorted[mid]相同的元素
	开始假设有mid - left + 1个,去掉比sorted[mid]小的,剩下的就是插入左孩子的
	*/
	for(i = left; i <= right; i++)//判断
	{
		if(t[p].val[i] < sorted[mid])
			isame--;
	}
	int l = left, r = mid + 1;
	for(i = left; i <= right; i++)
	{
		if(i == left)
			t[p].num[i] = 0;
		else
			t[p].num[i] = t[p].num[i - 1];

		if(t[p].val[i] < sorted[mid]) //小于,进入左孩子
		{
			t[p].num[i]++;
			t[p + 1].val[l++] = t[p].val[i];
		}
		else if(t[p].val[i] > sorted[mid])  //大于,进入右孩子
			t[p + 1].val[r++] = t[p].val[i];
		else             //等于,判断是否存在相等,没有,进入右孩子,有,进入左孩子
		{
			if(isame)
			{
				isame--;
				t[p].num[i]++;
				t[p + 1].val[l++] = t[p].val[i];
			}
			else
			{
				t[p + 1].val[r++] = t[p].val[i];
			}
		}
	}
	build(left, mid, p + 1);
	build(mid + 1, right, p + 1);
}

int query(int a, int b, int k, int p, int left, int right)
{
	if(a == b)
		return t[p].val[a];
	int s, ss, mid = (left + right) / 2;
/*  s为区间[a,b]进入左孩子的个数
	ss为区间[left, a - 1]进入左孩子的个数,下面的也可以看得出来
	*/
	if(a == left)
	{
		s = t[p].num[b];
		ss = 0;
	}
	else
	{
		s = t[p].num[b] - t[p].num[a - 1];
		ss = t[p].num[a - 1];
	}

	if(s >= k)
	{
		a = left + ss;           //
		b = left + ss + s - 1;
		return query(a, b, k, p + 1, left, mid);
	}
	else    //递归右孩子,也就是找第 k-s大的数,因为前面有s个进入了左孩子
	{
		a = mid + 1 + a - left - ss;
		b = mid + 1 + b - left - t[p].num[b];
		return query(a, b, k - s, p + 1, mid + 1, right);
	}
}

int main()
{
	int T, n, m;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++)   //输入,
		{
			scanf("%d", &sorted[i]);
			t[0].val[i] = sorted[i];
		}
		sort(sorted + 1, sorted + n + 1);
		build(1, n, 0);    //建树
		int a, b, k;
		for(int j = 0; j < m; j++)  //查找
		{
			scanf("%d%d%d", &a, &b, &k);
			printf("%d\n", query(a, b, k, 0, 1, n));
		}
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-29 16:33:14

杭电ACM2665——Kth number~~划分树的相关文章

poj 2104 K-th Number(划分树模板)

划分树模板题,敲上模板就ok了. #include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<queue> #include<stack> #include<map> #include<set> #define MP

poj 2104 K-th Number(划分树)

题目链接:http://poj.org/problem?id=2104 题目分析:该问题给定一段区间中的值,再给定一段查询区间[ql, qr],需要给出该查询区间中的值在排序后的第K大的值: 使用划分树即可解决该问题:划分树的建树的复杂度为O(NlogN),查询一个区间的第K大值的复杂度为O(logN): 代码如下: #include <cstdio> #include <iostream> #include <algorithm> using namespace st

hdu 2665 Kth number(划分树)

题意:给定一列数,每次查询区间[s,t]中的第k大: 参考:http://www.cnblogs.com/kane0526/archive/2013/04/20/3033212.html http://www.cnblogs.com/kuangbin/archive/2012/08/14/2638829.html 思路:快排思想+线段树=划分树,也就是树的每一层都按规则划分: 对于本题,建树前,保存原数列排序后的数列,原数列作为树的顶层: 建树时,考虑当前区间中的中间值,若大于中间值,在下一层中

poj 2104 K-th Number (划分树入门)

题意:给n个数,m次询问,每次询问L到R中第k小的数是哪个 算法:划分树 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 using namespace std; 6 7 const int mx=1e5+5; 8 int tree[30][mx]; 9 int sortt[mx]; 10 int sum[30][mx]; 11 12 vo

hdu 2665 Kth number (poj 2104 K-th Number) 划分树

划分树的基本功能是,对一个给定的数组,求区间[l,r]内的第k大(小)数. 划分树的基本思想是分治,每次查询复杂度为O(log(n)),n是数组规模. 具体原理见http://baike.baidu.com/link?url=vIUKtsKYx7byeS2KCOHUI14bt_0sdHAa9BA1VceHdGsTv5jVq36SfZgBKdaHYUGqIGvIGrE_aJtqy0D0b1fCoq 个人感觉看代码是最好的学习方法. #include <cstdio> #include <c

POJ2104 K-th Number 划分树 模板题啊

/*Source Code Problem: 2104 User: 96655 Memory: 14808K Time: 1282MS Language: G++ Result: Accepted Source Code*/ #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<stack>

poj2104--K-th Number(划分树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 40169   Accepted: 13120 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

K-th Number 线段树(归并树)+二分查找

K-th Number 题意:给定一个包含n个不同数的数列a1, a2, ..., an 和m个三元组表示的查询.对于每个查询(i, j, k), 输出ai, ai+1, ... ,aj的升序排列中第k个数 . 题解:用线段树,每个节点维护一个区间并且保证内部升序,对于每次查询x,返回该区间小于x的数的个数.就这样不断二分,直到找到x为止. 线段树(归并树)+二分查找 1 #include <iostream> 2 #include <cstdio> 3 #include <

POJ2104 K-th Number[主席树]

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 51440   Accepted: 17594 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse