Super Mario HDU - 4417(主席树解决区间数字小于k的个数||线段树+离线)

本文探讨了游戏关卡设计中遇到的挑战,特别是针对马里奥游戏中的复杂路径规划。通过引入主席树和线段树的数据结构,解决了查询特定区间内满足条件元素数量的问题,提升了游戏性能。

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

Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss’s castle as a line (the length is n), on every integer point i there is a brick on height hi. Now the question is how many bricks in [L, R] Mario can hit if the maximal height he can jump is H.
Input
The first line follows an integer T, the number of test data.
For each test data:
The first line contains two integers n, m (1 <= n <=10^5, 1 <= m <= 10^5), n is the length of the road, m is the number of queries.
Next line contains n integers, the height of each brick, the range is [0, 1000000000].
Next m lines, each line contains three integers L, R,H.( 0 <= L <= R < n 0 <= H <= 1000000000.)
Output
For each case, output "Case X: " (X is the case number starting from 1) followed by m lines, each line contains an integer. The ith integer is the number of bricks Mario can hit for the ith query.
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
求l到r中小于k的数字个数。
对于这种l到r的八九不离十是线段树或者主席树,根据题目而定。
这个题目就是利用主席树,由于数据量比较大,所以需要离散化一下。关于离散化和不离散化对于主席树的影响,离散化之后,我们在插入数据的时候,是根据数据在数组中的相对位置,就是lower_bound之后的数。但是不离散化,我们就可以直接按着权值插入数据。权值线段树也是酱紫。我在做主席树或者权值线段树的时候,有时就分不清该是用位置还是权值,就很懵逼。感觉这样判断还是比较正确的,不对的话希望大佬可以指正。
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#define ll long long
using namespace std;

const int maxx=1e5+100;
struct node{
	int l;
	int r;
	int v;
}p[maxx*40];
int a[maxx],b[maxx],root[maxx];
int n,m,num;

int build(int l,int r)
{
	int cur=num++;
	p[cur].v=0;
	if(l==r) return cur;
	int mid=l+r>>1;
	p[cur].l=build(l,mid);
	p[cur].r=build(mid+1,r);
	return cur;
}
int update(int rot,int l,int r,int k)
{
	int cur=num++;
	p[cur]=p[rot];
	p[cur].v++;
	if(l==r) return cur;
	int mid=l+r>>1;
	if(k<=mid) p[cur].l=update(p[rot].l,l,mid,k);
	else p[cur].r=update(p[rot].r,mid+1,r,k);
	return cur;
}
int query(int lrot,int rrot,int l,int r,int k)
{
	if(l==r) return p[rrot].v-p[lrot].v;//到达叶子节点之后,就直接做差就行。
	int mid=l+r>>1;
	if(k<=mid) return query(p[lrot].l,p[rrot].l,l,mid,k);
	else 
	{
		int ans=p[p[rrot].l].v-p[p[lrot].l].v;
		ans+=query(p[lrot].r,p[rrot].r,mid+1,r,k);
		return ans;
	}
}
int main()
{
	int t,pos,l,r,k,kk=0;
	scanf("%d",&t);
	while(t--)
	{
		num=1;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
		sort(b+1,b+1+n);
		int len=unique(b+1,b+1+n)-b-1;//离散化
		root[0]=build(1,len);
		for(int i=1;i<=n;i++)
		{
			pos=lower_bound(b+1,b+1+len,a[i])-b;
			root[i]=update(root[i-1],1,len,pos);
		}
		printf("Case %d:\n",++kk);
		while(m--)
		{
			scanf("%d%d%d",&l,&r,&k);
			l++;r++;
			pos=upper_bound(b+1,b+1+len,k)-b-1;
			if(pos==0) puts("0");
			else printf("%d\n",query(root[l-1],root[r],1,len,pos));
		}
	}
	return 0;
}

过来更新一波。这个题也可以不用主席树,也可以用线段树+离线去做。
我们把所有的查询按着k值大小排序,记录之前的位置。也要把原序列按权值大小排序,记录之前的位置。然后遍历去遍历数组。当当前值小于当前的k时,就按着原来的位置插入线段树,否则就直接按着区间找出答案。
代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxx=1e5+100;
struct Node{
	int h,pos;
	bool operator <(const Node &a)const{
		return a.h>h;
	}
}a[maxx];
struct Node1{
	int l,r,h,id;
	bool operator <(const Node1 &a)const{
		return a.h>h;
	}
}b[maxx];
struct node{
	int l;
	int r;
	int sum;
}p[maxx<<2];
int ans[maxx];
int n,m;

inline void pushup(int cur)
{
	p[cur].sum=p[cur<<1].sum+p[cur<<1|1].sum;
}
inline void build(int l,int r,int cur)
{
	p[cur].l=l;
	p[cur].r=r;
	p[cur].sum=0;
	if(l==r) return ;
	int mid=l+r>>1;
	build(l,mid,cur<<1);
	build(mid+1,r,cur<<1|1);
}
inline void update(int pos,int v,int cur)
{
	int L=p[cur].l;
	int R=p[cur].r;
	if(L==R)
	{
		p[cur].sum+=v;
		return ;
	}
	int mid=L+R>>1;
	if(pos<=mid) update(pos,v,cur<<1);
	else update(pos,v,cur<<1|1);
	pushup(cur);
}
inline int query(int l,int r,int cur)
{
	int L=p[cur].l;
	int R=p[cur].r;
	if(l<=L&&R<=r) return p[cur].sum;
	int mid=L+R>>1;
	if(r<=mid) return query(l,r,cur<<1);
	else if(l>mid) return query(l,r,cur<<1|1);
	else return query(l,mid,cur<<1)+query(mid+1,r,cur<<1|1);
}
int main()
{
	int t,k=0;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);build(1,n,1);
		for(int i=1;i<=n;i++) scanf("%d",&a[i].h),a[i].pos=i;
		for(int i=1;i<=m;i++) scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].h),b[i].l++,b[i].r++,b[i].id=i;
		sort(a+1,a+1+n);sort(b+1,b+1+m);
		for(int i=1,j=1;i<=m;i++)
		{
			while(a[j].h<=b[i].h&&j<=n) update(a[j++].pos,1,1);
			ans[b[i].id]=query(b[i].l,b[i].r,1);
		}
		printf("Case %d:\n",++k);
		for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	}
}

努力加油a啊,(o)/~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starlet_kiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值