主席树(结构体实现,代码简单)

本文深入探讨了主席树的数据结构,通过分析大佬们的思路和代码,提供了一套简单易懂的主席树实现方法。以HDU 2665题为例,详细讲解了如何使用主席树解决区间第K大问题,代码附带完整注释。

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

今天又学习主席树,看网上的博客,大佬们思路很清晰,看到代码的时候,惊了~

什么和什么啊 ,l数组,r数组,rot数组,,遍地开花,于是自己手动了一个结构体主席树   代码还凑合  

先看别人家的思路,看完思路再来我这里看全网最简单易懂代码~哈哈哈哈

思路博客:https://www.cnblogs.com/zyf0163/p/4749042.html

 

代码是模板题  给一个数组 求区间第K大http://acm.hdu.edu.cn/showproblem.php?pid=2665

 

#include<bits/stdc++.h>
using namespace std;
struct Node{int l,r,sum;} t[4000000];
int a[100001],b[100001],root[100001],n,m,T,sz,cnt,ll,rr,w;
void build(int & o,int l,int r){//建立一个空树,从建树的过程来看,数据范围一定要确定,卧槽(这数据结构太傻了) 
	o=++cnt;
	t[o].sum=0;//这个树的个数为0
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(t[o].l,l,mid);//和线段树不同的是,左右的根不再是区间的左右边界了,
	build(t[o].r,mid+1,r);//而是指向的不同的根。也就是第一个是总根  t[2]是总根的左子树的根 t[3] 是右子树的根 
}
void update(int &o,int l,int r,int last,int v){
	o=++cnt;//开辟新的根节点
	t[o].l=t[last].l;
	t[o].r=t[last].r;
	t[o].sum=t[last].sum+1;
	if(l==r)return ;
	int mid=(l+r)>>1;
	if(v<=mid) update(t[o].l,l,mid,t[last].l,v);//对左边 
	else update(t[o].r,mid+1,r,t[last].r,v);
} 
int ask(int pre,int now,int l,int r,int k){
	if(l==r)return l;
	int mid=(l+r)>>1,num=t[ t[now].l ].sum-t [ t[pre].l ].sum;
	if(k<=num)return ask(t[ pre ].l,t[now].l,l,mid,k);
	else return ask(t[pre].r,t[now].r,mid+1,r,k-num); 
}
int main(){
	scanf("%d",&T);
	while(T--){
		cnt=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			b[i]=a[i];
		}
		sort(b+1,b+n+1);
		sz=unique(b+1,b+n+1)-(b+1);//数据个数 
		
		//cout<<sz<<endl;
		//for(int i=1;i<=n;i++)cout<<b[i]<<" ";cout<<endl;
		
		for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+sz+1,a[i])-b;
		build(root[0],1,sz);//对数据范围进行建一颗空树

		//for(int i=0;i<=4*n;i++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d\n",i,root[i],t[i].l,t[i].r,t[i].sum);

		for(int i=1;i<=n;i++)update(root[i],1,sz,root[i-1],a[i]);//线段树插入over 

		//for(int i=0;i<=5*n;i++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d\n",i,root[i],t[i].l,t[i].r,t[i].sum);

		while(m--){
			scanf("%d%d%d",&ll,&rr,&w);
			printf("%d\n",b[ask(root[ll-1],root[rr],1,sz,w)]);//为什么调10几分钟bug??因为离散化要离散回来 
		}
	}
	return 0;
} 

  一发AC~  哈哈哈

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值