poj 2104 hdu 2665 区间第k大 可持久化线段树

好像又可以叫函数式线段树,反正主要思想就是充分利用历史信息,共用空间,具体的看论文吧

这里讲的比较详细

http://hi.baidu.com/wyl8899/item/e00796a9cb2df73d020a4d68

这题A的太爽了* _ *

看CLJ标称的时候用的是动态申请内存的线段树,非常不习惯- -

昨天听说可以静态实现,而且很简单,于是重新想了下实现的思路,果断开敲,敲之前把各种细节,包括如何调试都想的比较清楚,然后实现起来真的是有如行云流水,呵呵,有一种新的想法,然后通过不断的实践去实现它,这种感觉真好啊

好了,就献上我的代码吧,我写线段树就是懒得开结构体。。。

注:原先写的代码有个bug,poj数据太水,被我过了,在hdu wa了,现在已经修正

献上能在hdu AC的代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int ls[maxn*20],rs[maxn*20],sum[maxn*20];
int T[maxn],tot,rt;
void build(int l,int r,int &rt) 
{
	rt=++tot;
	sum[rt]=0;
	if(l==r) return ;
	int m=l+r>>1;
	build(l,m,ls[rt]);
	build(m+1,r,rs[rt]);
}
void update(int last,int p,int l,int r,int &rt)
{
	rt=++tot;
	ls[rt]  = ls[last];   rs[rt]  = rs[last];   sum[rt] = sum[last] + 1;
	if(l==r) return ;
	int m=l+r>>1;
	if(p<=m) update(ls[last],p,l,m,ls[rt]);
	else update(rs[last],p,m+1,r,rs[rt]);
}
int query(int ss,int tt,int l,int r,int k)
{
	if(l==r) return l;
	int m=l+r>>1;
	int cnt=sum[ls[tt]] - sum[ls[ss]];
	if(k <= cnt)
		return query(ls[ss],ls[tt],l,m,k);
	else 
		return query(rs[ss],rs[tt],m+1,r,k-cnt);
}
int num[maxn],san[maxn],n,m;
void gogogo()
{
	int l,r,k;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&num[i]);
		san[i]=num[i];
	}
	tot=0;
	sort(san+1,san+n+1);
	int cnt=unique(san+1,san+n+1)-san-1;
	build(1,cnt,T[0]);
	for(int i=1;i<=n;i++) num[i]=lower_bound(san+1,san+cnt+1,num[i])-san;
	for(int i=1;i<=n;i++) 	update(T[i-1],num[i],1,cnt,T[i]);
	while(m--)
	{
		scanf("%d%d%d",&l,&r,&k);
		int id=query(T[l-1],T[r],1,cnt,k);
		printf("%d\n",san[id]);
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--) 
	{
		scanf("%d%d",&n,&m);
		gogogo();
	}
	return 0;
}



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值