hdu 4417 Super Mario​​​​​​​(主席树)

本文介绍了一种使用主席树解决区间中小于等于特定值元素数量的问题。通过实例讲解了主席树的基本概念、构建方法及查询过程,并提供了完整的C++代码实现。

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

题目链接:hdu 4417 Super Mario

Sample Input
1
10 10
0 5 2 7 5 4 3 8 7 7 
2 8 6
3 5 0
1 3 1
1 9 4
0 1 0
3 5 5
5 5 1
4 6 3
1 5 7
5 7 3
 

Sample Output
Case 1:
4
0
0
3
1
2
0
1
5
1

 题意:给一个数组,求区间中小于等于h的数字个数

思路:裸的主席树,第一次用大致说一下自己的理解

主席树就是很多的线段树,当一个点更新的时候(上图的5更新),影响的是一条链(1-2-5),主席树保留了不需要更新的结点,第i棵线段树维护1~i的区间信息

 这一题中,0 5 2 7 5 4 3 8 7 7,离散化后得 0 2 3 4 5 7 8对应下标1 2 3 4 5 6 7,在insert操作结束之后会得到下图所示的树,圆圈中的数字表示这个区间中的数字个数,存储在size[i]

 要记住size存储的是数字个数,叶子结点是排序后离散化的下标,即权值,其他的写法的改变,按照线段树的写法来即可

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100005

int root[N];//root[i]表示第i课线段树 
int size[N*25],lchild[N*25],rchild[N*25];
int tot;

void insert(int last,int cur,int L,int R,int k) //单点更新 
{
    size[cur]=size[last]+1;//将前一个树的信息复制过来 
    lchild[cur]=lchild[last];
    rchild[cur]=rchild[last]; 
    if(L==R)return ;
    int mid=L+R>>1;
    if(k<=mid) insert(lchild[last],lchild[cur]=++tot,L,mid,k);//对于需要更改的节点都需要新增节点
    else insert(rchild[last],rchild[cur]=++tot,mid+1,R,k);
}
int query(int last,int cur,int l,int r,int L,int R)
{
	if(l>r)return 0;
	if(L==l&&r==R){
		return size[cur]-size[last];
	}
	int mid=(L+R)>>1;
	if(l>mid)return query(rchild[last],rchild[cur],l,r,mid+1,R);
	else if(r<=mid)return query(lchild[last],lchild[cur],l,r,L,mid);
	else return query(rchild[last],rchild[cur],mid+1,r,mid+1,R)+query(lchild[last],lchild[cur],l,mid,L,mid);
}
int a[100005];
int b[100005];
int main(){
	int t;
	scanf("%d",&t);
	int c=1;
	while(t--){
		int n,m;
		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);//排序 
		int nn=unique(b+1,b+n+1)-b-1;//去重
		//上面是在离散化处理 
		tot=0;
		for(int i=1;i<=n;i++){
			int k=lower_bound(b+1,b+nn+1,a[i])-b;//找到a[i]在整个数列中是第几大
			insert(root[i-1],root[i]=++tot,1,nn,k);
		}
		printf("Case %d:\n",c++);
		for(int i=1;i<=m;i++){
			int l,r,h;
			scanf("%d%d%d",&l,&r,&h);
			l++;r++;
			int k=upper_bound(b+1,b+1+nn,h)-b-1;//找到h在整个数列中是第几大 
			printf("%d\n",query(root[l-1],root[r],1,k,1,nn)); 
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值