POJ2104 K-th Number——划分树——pku2104

按照铎铎大牛的AC Record做的第一题,就被虐得很惨。

这是划分树的模板题。划分树,主要是求无更改的情况下区间第k小值。建树和线段树有些类似,但是划分树是在排序的基础上,左子树记录比中间值小的值,右子树记录比中间值大的数,在每棵子树中不改变原数列中的顺序。

查找也类似线段树,规定查找区间,定义函数Find(code,l,r,kth,dep),code为当前查找到的节点值,l,r是区间左右界,为闭区间,dep代表当前查找的深度。如果l,r与code的管辖区间相同就返回b[l+kth-1](b为排好序的数列),否则令t1、t2为[tree[code].left,l-1]进入左子树的数的个数和[tree[code].left,r]进入左子树的个数。

如果t1-t2>=kth,就查找左子树,边界为[tree[code].left+tree[code].left+t2,tree[code].left+tree[code].left+t1-1]

(也就是[tree[code].left+[tree[code].left,l-1]区间进入左子树的数的个数,tree[code].left+[tree[code].left,r]进入左子树的数的个数]),

否则查找右子树,边界为[tree[code].mid+1+l-tree[code].left-t2,tree[code].mid+1+r-tree[code].left-t1]

(也就是[tree[code].mid+[tree[code].left,l-1]区间进入右子树的数的个数,tree[code].mid+[tree[code].left,r]进入右子树的数的个数])

 

有点繁琐,自己推导一下就行了。

 

代码:

Program poj2104;//By_Thispoet
Const 
	maxn=100005;
Var
	i,j,k,m,n,p,q,x,y,z				:Longint;
	g,rel							:Array[0..20,0..maxn]of Longint;
	b								:Array[1..maxn]of Longint;
	tree							:ARray[1..maxn*10]of record left,right,mid:Longint; end;
	
Procedure Qsort(l,r:Longint);
var i,j,k,temp:Longint;
begin
	i:=l;j:=r;k:=b[i+random(j-i+1)];
	repeat
		while b[i]<k do inc(i);
		while b[j]>k do dec(j);
		if i<=j then 
			begin
				temp:=b[i];b[i]:=b[j];b[j]:=temp;
				inc(i);dec(j);
			end;
	until i>j;
	if l<j then Qsort(l,j);
	if i<r then Qsort(i,r);
end;
	
	
Procedure Build_Tree(code,l,r,dep:Longint);
var i,j,k,ll,rr:Longint;
begin
	with tree[code] do 
		begin
			left:=l;right:=r;mid:=(l+r)>>1;
			if l=r then exit;
			ll:=l-1;rr:=mid;
			for i:=l to r do 
				begin
					if (g[dep-1,i]<=b[mid])and(ll<mid)then 
						begin
							inc(ll);
							g[dep,ll]:=g[dep-1,i];
							if l>i-1 then rel[dep,i]:=1 else rel[dep,i]:=rel[dep,i-1]+1;
						end else
							begin
								inc(rr);
								g[dep,rr]:=g[dep-1,i];
								if l>i-1 then rel[dep,i]:=0 else rel[dep,i]:=rel[dep,i-1];
							end;
				end;
			Build_Tree(code*2,l,mid,dep+1);
			Build_Tree(code*2+1,mid+1,r,dep+1);
		end;
end;
	
	
Function Find(code,l,r,k,dep:Longint):Longint;
var t1,t2,tnum,ll,rr:Longint;
begin
	if (tree[code].left=l)and(tree[code].right=r)then exit(b[l+k-1]);
	t1:=rel[dep,r];
	if tree[code].left>l-1 then t2:=0 else t2:=rel[dep,l-1];
	tnum:=t1-t2;
	if tnum>=k then 
		begin
			ll:=tree[code].left+t2;
			rr:=tree[code].left+t1-1;
			find:=Find(code*2,ll,rr,k,dep+1);
		end else 
			begin
				ll:=tree[code].mid+1+l-tree[code].left-t2;
				rr:=ll-l+r-tnum;
				find:=Find(code*2+1,ll,rr,k-tnum,dep+1);
			end;
end;
	
	
BEGIN

	readln(n,m);
	randomize;
	for i:=1 to n do
		begin
			read(g[0,i]);
			b[i]:=g[0,i];
		end;
	Qsort(1,n);
	Build_Tree(1,1,n,1);
	while m>0 do 
		begin
			readln(x,y,z);
			writeln(Find(1,x,y,z,1));
			dec(m);
		end;

END.

转载于:https://www.cnblogs.com/Thispoet/archive/2011/09/29/2195689.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值