划分树学习(poj 2104,hdu 3473)

原创 2011年08月12日 11:11:33

线段树一维的刷差不多了,求区间第K数一直卡着。

划分树和归并树都可以求,比较了一下时间效率,划分树比归并树快了很多,而且POJ有个求区间第K数的题用归并树居然过不去。

鉴于时间短,我决定把划分树给弄明白= =。。借用下小HH的图。


划分树和归并树都是用线段树作为辅助的,原理是基于 快排 和 归并排序 的。

划分树的建树过程基本就是模拟快排过程,取一个已经排过序的区间中值,然后把小于中值的点放左边,大于的放右边。并且记录d层第i个数之前(包括i)小于中值的放在左边的数。具体看下面代码注释。

关键是询问过程,具体见图。CSDN现在上传图片质量好差啊啊啊啊啊啊 。



hdu3473是求中位数的(可以证得,差值和最小的一定是中位数(如果是偶数个的话,中间两个任意以一个均可)),但是涉及求和,涉及求得在区间[l,r]中,中位数之前元素的和(排好序后)。这样的话,在建树过程增加一个二维数组,第d层下标<=i之前元素的和。然后找的时候,求区间差值和即可。

附,poj2104代码,买一赠一,poj2761和2104基本一样

#include <queue>
#include <stack>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <limits.h>
#include <string.h>
#include <string>
#include <algorithm>
#define MID(x,y) ( ( x + y ) >> 1 )
#define L(x) ( x << 1 )
#define R(x) ( x << 1 | 1 )
#define BUG puts("here!!!")
#define LL long long
using namespace std;

const int MAX = 100010;
LL s[MAX];
class Parti_tree{
public : 
	class Tnode{							// 我的一维线段树定义 
	public :
	    int l,r;
	    int len() { return r - l;}
	    int mid() { return MID(l,r);}
	    bool in(int ll,int rr) { return l >= ll && r <= rr; }
	    void lr(int ll,int rr){ l = ll; r = rr;}
	};
	Tnode node[MAX<<2];					
	int num_left[20][MAX], seg[20][MAX],sa[MAX];//sa数组存sort后的结果
						//seg数组存的是d层划分后的数字 (类似快排Partation (d-1)次后的结果) 
						//num_left存的是d层在i之前(包括i)小于 sa[mid] 的数的数目 
	void init()
	{
		memset(seg,0,sizeof(seg)); 
		memset(num_left,0,sizeof(num_left));
		memset(node,0,sizeof(node));
	}
	void build(int s,int t)
	{
		sort(sa+s,sa+t+s);
		Parti_build(1,s,t,1);
	}
    int query(int s,int t,int k)
	{
		return find_rank(1,s,t,1,k);
	}
	void Parti_build(int t,int l,int r,int d)
	{
		node[t].lr(l, r);
		if( node[t].len() == 0 ) return ;
		int mid = MID(l, r), lsame = mid - l + 1;
		for(int i=l; i<=r; i++)//首先确定分到每一侧的数的数目 
			if( seg[d][i] < sa[mid] )//因为相同的元素可能被分到两侧 
				lsame--; 
		int lpos = l,rpos = mid+1;
		for(int i=l; i<=r; i++)
		{
			if( i == l )
				num_left[d][i] = 0;
			else
				num_left[d][i] = num_left[d][i-1];
			if( seg[d][i] < sa[mid] )
			{
				num_left[d][i]++;
				seg[d+1][lpos++] = seg[d][i];
			}
			if( seg[d][i] > sa[mid] )
				seg[d+1][rpos++] = seg[d][i];
			if( seg[d][i] == sa[mid] )
				if( lsame > 0 )	// 如果大于0,说明左侧可以分和sa[mid]相同的数字 
				{		
					lsame--;
					num_left[d][i]++;
					seg[d+1][lpos++] = seg[d][i];
				}
				else			//反之,说明左侧数字已经分满了,就分到右边去 
					seg[d+1][rpos++] = seg[d][i];
		}
		Parti_build(L(t), l, mid, d+1);
		Parti_build(R(t), mid+1, r, d+1);	
	}
	int find_rank(int t,int l,int r,int d,int val)
	{
		if( node[t].len() == 0 ) return seg[d][l];
		int s,ss;				//s表示区间[l,r]有多少个小于sa[mid]的数被分到左边 
		if( l == node[t].l )
			ss = 0;
		else
			ss = num_left[d][l-1];
		s = num_left[d][r] - ss;//ss表示从当前区间的L到l-1有多少个小于sa[mid]的数被分到左边 
		if( s >= val )
			return find_rank(L(t), node[t].l+ss, node[t].l+ss+s-1, d+1, val);
		else
		{
			int mid = node[t].mid();
			int bb = l - node[t].l - ss;	//表示从当前区间L到l-1有多少个分到右边 
			int b = r - l + 1 - s;			//表示[l,r]有多少个分到右边 
			return find_rank(R(t), mid+bb+1, mid+bb+b,d+1,val-s);
		}
	}
};
 
Parti_tree t;
int main()
{
	int n,m,x,y,k;
	
	while( ~scanf("%d%d",&n,&m) )
	{
		t.init();
		for(int i=1; i<=n; i++)
		{
			scanf("%d",&t.sa[i]);
			t.seg[1][i] = t.sa[i];
		}
		t.build(1,n);
		while( m-- )
		{
			scanf("%d%d%d",&x,&y,&k);
			int ans = t.query(x, y, k);
			printf("%d\n",ans);
		}
	}

return 0;
}


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

区间第K大数——划分树(POJ2104解题报告)

百度百科:划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时间复杂度内)序列区间的第k大值。 划分树的基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数...
  • Acceptedxukai
  • Acceptedxukai
  • 2011年11月04日 21:05
  • 4404

划分树学习(poj 2104,hdu 3473)

线段树一维的刷差不多了,求区间第K数一直卡着。 划分树和归并树都可以求,比较了一下时间效率,划分树比归并树快了很多,而且POJ有个求区间第K数的题用归并树居然过不去。 鉴于时间短,我决定把划分树给...
  • zxy_snow
  • zxy_snow
  • 2011年08月12日 11:11
  • 11366

poj2104(划分树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 32718   ...
  • xj2419174554
  • xj2419174554
  • 2013年08月30日 20:06
  • 857

POJ 2104 K-th Number(区间第k大数)(平方分割,归并树,划分树)

题目链接: http://poj.org/problem?id=2104 解题思路: 因为查询的个数m很大,朴素的求法无法在规定时间内求解。因此应该选用合理的方式维护数据来做到高效地查询。 如果x是...
  • piaocoder
  • piaocoder
  • 2015年08月25日 10:56
  • 1344

poj2104[划分树问题]

这个题目的意思是给你一个区间,让你找到在某个给出的子区间当中第k大的数。 这是我的第一个划分树题目,因为之前做了好多线段树的题目了,看了看书上基本的代码,然后按照自己的思路写了出来。 按照这个题目...
  • zq17865815296
  • zq17865815296
  • 2016年10月25日 15:46
  • 95

划分树(模板)poj2104

Language: Default K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total S...
  • u010660276
  • u010660276
  • 2014年08月07日 21:45
  • 367

【划分树】HDU 3473

题意就是求[l,r]中位数与其余元素差的绝对值再求和 方法:用划分树求出中位数,询问的同时求出中位数左边元素和还有左边元素个数,因此这里需要增加一个数组suml[dep][i]表示深度为dep的一段...
  • leolin_
  • leolin_
  • 2012年02月16日 14:24
  • 663

hdu 2665 (poj 2104) 划分树

poj2104题目要求弱一些,n个数是不同的。hdu2665中没有这样的限制。所以在划分左右区间时需要处理一下相同的数怎么划分。 #include #include #include #define...
  • Balloons2012
  • Balloons2012
  • 2012年10月10日 15:53
  • 420

poj 2104 K-th Number(划分树)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 28725   ...
  • fp_hzq
  • fp_hzq
  • 2012年09月18日 20:56
  • 966

划分树的用法(一):查询区间第K大值值(poj2104)

划分树的用法(一):查询区间第K大值值(poj2104) 2012-09-28 09:05:22 标签:划分树 poj2104 动态区间求Kth值 原创作品,允许转载,转载时请务必以...
  • pi9nc
  • pi9nc
  • 2013年05月30日 22:20
  • 1971
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:划分树学习(poj 2104,hdu 3473)
举报原因:
原因补充:

(最多只允许输入30个字)