POJ 2104 划分树

本文深入探讨了线段树在解决区间第K数问题时的实现细节与效率对比,重点介绍了划分树的构建与查询过程,并通过实例展示了其在POJ题目中的应用及与归并树的性能比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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


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

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

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


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

const int MAXN=100005;
const int DEPTH=20;
int less_mid[DEPTH][MAXN];
int seg[DEPTH][MAXN];
int data[MAXN];
int n;
struct seg_tree
{
	int l,r;
	void init(int tl,int tr)
	{
		l=tl,r=tr;
	}
}tr[MAXN<<2];

void build(int l,int r,int root,int d)
{
	tr[root].init(l,r);
	if(l==r)
	{
		return ;
	}
	int mid=(l+r)>>1;
	int lsame=mid-l+1;
	int i;
	for(i=l;i<=r;i++)
	{
		if(seg[d][i]<data[mid])
		{
			lsame--;
		}
	}
	int lson=l,rson=mid+1,same=0;
	for(i=l;i<=r;i++)
	{
		if(i==l)
		{
			less_mid[d][i]=0;
		}
		else
		{
			less_mid[d][i]=less_mid[d][i-1];
		}
		if(seg[d][i]<data[mid])
		{
			less_mid[d][i]++;
			seg[d+1][lson++]=seg[d][i];
		}
		else if(seg[d][i]>data[mid])
		{
			seg[d+1][rson++]=seg[d][i];
		}
		else if(same<lsame)
		{
			same++;
			less_mid[d][i]++;
			seg[d+1][lson++]=seg[d][i];
		}
		else
		{
			seg[d+1][rson++]=seg[d][i];
		}
	}
	build(l,mid,root<<1,d+1);
	build(mid+1,r,root<<1|1,d+1);
}

int update(int l,int r,int root,int d,int k)
{
	if(l==r)
	{
		return seg[d][l];
	}
	int s,ss;
	if(l==tr[root].l)
	{
		s=less_mid[d][r];
		ss=0;
	}
	else
	{
		s=less_mid[d][r]-less_mid[d][l-1];
		ss=less_mid[d][l-1];
	}
	if(s>=k)
	{
		return update(tr[root].l+ss,tr[root].l+ss+s-1,root<<1,d+1,k);
	}
	else
	{
		int mid=(tr[root].l+tr[root].r)>>1;
		int bb=l-tr[root].l-ss;
		int b=r-l-s+1;
		return update(mid+bb+1,mid+bb+b,root<<1|1,d+1,k-s);
	}
}

int main()
{
	int m;
	while(~scanf("%d%d",&n,&m))
	{
		int i;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&seg[1][i]);
			data[i]=seg[1][i];
		}
		sort(data+1,data+1+n);
		build(1,n,1,1);
		int l,r,k;
		while(m--)
		{
			scanf("%d%d%d",&l,&r,&k);
			printf("%d\n",update(l,r,1,1,k));
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值