pku 2104划分树

     之前未做过划分树的题,今日撞上,得探个究竟;从网上找资料看看,觉得HH大牛上的博客写得不错  

     附上网址:http://www.notonlysuccess.com/index.php/divide-tree/

    这里我简单介绍下它的入门,都知道它在求序列区间上的第K最大值有log(n)的算法,不过它所求的是离线的,所谓离线就是数据给定后就不会进行对数据进行改变!

    划分树主要的思想就是快排,线段树只是被当作工具利用而已!整体方法如下:对输入的数据副本进行排序,这样对应线段树区间的中位数就知道是多少了,也就是说可以知道线段树区间里的元素是往左放还是往右放,划分树的建立通常要在线段树建立上再加一个参数:层数d,因为要对每层的划分结果进行保存;

给定   划分树层结果保存数组  var[d][N]   第0层的数据已经输入;数据排序数组sorted[N]; 对每层d对应的各个区间【L,r]里对每个 L<=i<=r ,let[d][i] 表示区间里【l,i]里有多少个元素要放到左子树(该区间里的元素是对这一层的数据,记录这个是为查找的时候进行区间更新!) 以及线段树T[4*N]; 

      建立函数: void build(int left,int right,int d,int k)

                            {

                                           当left == right   返回

                                           mid = 区间中点

                                          (如果给定的数据有重复元素的话,我们也就需要知道跟此区间中点的元素相同的有多少个要放到左子树,有多少个放到)

                                              左子树应该放(mid-l+1)个元素,少于sorted[mid]的元素有a个(a 是对区间元素遍历得来的),

                                                那么还有 mid-l+1-a个元素是放sorted[mid];

                                               for i=l to r

                                                       先赋值let[d][i] = let[d][i-1] ,(后续计算中只需要知道第i个元素是放左子树还是右子树就可对let[d][i]进行更新)

                                                      下标为i的元素是放左子树还是放右子树,通过与sorted[mid]比较则可得到,

                                                       当然遇到等于sorted[mid]的元素要看左子树是否还能放它

                                                       放到左子树的元素要使let[d][i] ++;

                                             end 

                                         递归建立下一层!(左子树和右子树)

                             }

       查找区间【l,r】的最第K最大值: int find(l,r,K,d,k)

                                                                {

                                                                        l == r return var[d][l];

                                                                       看这个区间有多少人元素放左子树 lnum 有多少元素放右子树rnum

                                                                      看区间【T[k].l,l-1】有多少元素放左子树llnum,有多少元素放右子树rrnum

                                                                     如果lnum>=k   说明要找的元素在左子树,我们应该在哪个区间去找它的第K大值,

                                                                       显然一般不是整个左子树,因为还有不属于区间【l,r]的元素也放到了左子树,

                                                                       但我们会发现一点的是不属于区间【l,r】的元素要么排在左子树的前面,

                                                                        要么排在左子树的后面,而中间的区间 则是我们要查找的区间,

                                                                         显然简单计算就知道为【 T[k].l+llnum    ,T[k].l+llnum+lnum-1】

                                                                    同理如果要在右子树中查找的话,对应的区间应该是【T[k].mid + rrnum,T[k].mid+rrnum+rnum-1]                                                             

                                                                 }

附代码:

  

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <cstring>
#include <list>
#include <queue>
#include <stack>
#include <cmath>

using namespace std;

#define PF(x) (scanf("%d",&x))
#define PT(x,y) (scanf("%d%d",&x,&y))
#define PR(x) (printf("%d\n",x))
#define PRT(x,y)(printf("%d %d\n",x,y))
#define PB(x)(scanf("%I64d",&x))
#define PRB(x)(printf("%I64d\n",(x)))
#define For(i,n) for(int i=0;i<(n);i++)
#define CLR(ar) (memset(ar,0,sizeof(ar)))
#define CLR1(ar) (memset(ar,-1,sizeof(ar)))
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)>(y)?(y):(x)
#define L(x) (x<<1)
#define R(x) ((x<<1)|1)
#define Mid(x,y) ((x+y)>>1)

typedef __int64 LL;
#define N 100005
#define M 105
#define Mod 1000
#define Inf 0x7fffffff

struct tree
{
	int l,r;
	int m()
	{
		return (l+r)>>1;
	}
};
tree T[4*N];
int var[30][N];
int let[30][N];
int sorted[N];
int n,m;

void build(int l,int r,int d,int k)
{
	T[k].l = l;
	T[k].r = r;
	if(l == r) {return ;}
	int mid = T[k].m();
	int samemid = mid-l+1;
	for(int i=l;i<=r;i++)
	{
		if(var[d][i]<sorted[mid]) samemid--;
	}
	int lbound = l;
	int rbound = mid+1;
	int same = 0;

	for(int i=l;i<=r;i++)
	{
		if(i == l)
		{
			let[d][i] = 0;
		}
		else let[d][i] = let[d][i-1];
		if(var[d][i]<sorted[mid])
		{
			let[d][i]++;
			var[d+1][lbound++] = var[d][i];
		}
		else if(var[d][i]>sorted[mid])
		{
			var[d+1][rbound++] = var[d][i];
		}
		else
		{
			if(same<samemid)
			{
				var[d+1][lbound++] = var[d][i];same++;
				let[d][i]++;
			}
			else var[d+1][rbound++] = var[d][i];

		}
	}
	build(l,mid,d+1,L(k));
	build(mid+1,r,d+1,R(k));
}
int finds(int l,int r,int n_k,int d,int k)
{
	if(T[k].l == T[k].r ) return var[d][T[k].l];
	int lnum,rnum;
	if(l == T[k].l)
	{
		lnum = 0;
		rnum = let[d][r];
	}
	else 
	{
		lnum = let[d][l-1];
		rnum = let[d][r] - let[d][l-1];
	}
	if(rnum>=n_k)
	{
		int ll,rr;
		ll = T[k].l + lnum;
		rr = T[k].l + lnum+rnum-1;
		return finds(ll,rr,n_k,d+1,L(k));
	}
	else
	{
		int ll,rr;
		int llnum,rrnum;
		llnum = l-T[k].l-lnum;
		rrnum = r-l+1-rnum;
		ll = T[k].m()+llnum+1;
		rr = T[k].m()+llnum+rrnum;
		return finds(ll,rr,n_k-rnum,d+1,R(k));
	}
}
void init()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		For(i,n) {PF(var[0][i+1]);sorted[i+1] = var[0][i+1];}
		sort(sorted+1,sorted+n+1);
		build(1,n,0,1);
		For(i,m)
		{
			int l,r,n_k;
			PT(l,r);PF(n_k);
			PR(finds(l,r,n_k,0,1));
		}
	}
	return ;
}

int main()
{
	init();
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值