HDU1823-二维线段树

本题是二维线段树模板,题目链接:HDU1823-Luck and Love

我给出题目:

Problem Description
世界上上最远的距离不是相隔天涯海角
而是我在你面前
可你却不知道我爱你
                ―― 张小娴

前段日子,枫冰叶子给Wiskey做了个征婚启事,聘礼达到500万哦,天哪,可是天文数字了啊,不知多少MM蜂拥而至,顿时万人空巷,连扫地的大妈都来凑热闹来了。―_―|||
由于人数太多,Wiskey实在忙不过来,就把统计的事情全交给了枫冰叶子,自己跑回家休息去了。这可够枫冰叶子忙的了,他要处理的有两类事情,一是得接受MM的报名,二是要帮Wiskey查找符合要求的MM中缘分最高值。
 
Input
本题有多个测试数据,第一个数字M,表示接下来有连续的M个操作,当M=0时处理中止。
接下来是一个操作符C。
当操作符为‘I’时,表示有一个MM报名,后面接着一个整数,H表示身高,两个浮点数,A表示活泼度,L表示缘分值。 (100<=H<=200, 0.0<=A,L<=100.0)
当操作符为‘Q’时,后面接着四个浮点数,H1,H2表示身高区间,A1,A2表示活泼度区间,输出符合身高和活泼度要求的MM中的缘分最高值。 (100<=H1,H2<=200, 0.0<=A1,A2<=100.0)
所有输入的浮点数,均只有一位小数。
 
Output
对于每一次询问操作,在一行里面输出缘分最高值,保留一位小数。
对查找不到的询问,输出-1。
 
Sample Input
8
I 160 50.5 60.0
I 165 30.0 80.5
I 166 10.0 50.0
I 170 80.5 77.5
Q 150 166 10.0 60.0
Q 166 177 10.0 50.0
I 166 40.0 99.9
Q 166 177 10.0 50.0
0
 
Sample Output
80.5
50.0
99.9

直接给出AC代码吧,下面有详细注释:


#include <iostream>
#include <cstdio>
#include <stdio.h>
#include <cmath>
#define maxn 222
using namespace std;
//第二维度,注意m要放在第二维,因为一定要搜完第一维和第二维才能确定m的值
struct node{
	int al;//活泼度
	int ar;
	double m;//存结果
};
struct node1{
	int hl;身高
	int hr;
	node stu[4444];//在第一维里面建立到第二维的通道,用于连接第一维和第二维
}ans[maxn<<2];
void sub_build(int id,int rt,int al,int ar)//第二维线段树,记录活泼度
{
	ans[id].stu[rt].al=al;//通过第一维里面的通道stu来访问第二维线段树
	ans[id].stu[rt].ar=ar;
	ans[id].stu[rt].m=-1.0;//答案都初始化为-1.0
	if(al==ar)return;
	int m=(al+ar)>>1;
	sub_build(id,rt<<1,al,m);//在第一维线段树的基础上建立二维线段树,id是第一维的坐标,不能改变,rt是第二维线段树的坐标
	sub_build(id,rt<<1|1,m+1,ar);
	
}
//建立第一维线段树,记录的是身高h的情况
void build(int id,int hl,int hr,int al,int ar)
{
	ans[id].hl=hl;
	ans[id].hr=hr;
	sub_build(id,1,al,ar);//同时建立第二维线段树
	if(hl==hr)return;
	int m=(hl+hr)>>1;
	build(id<<1,hl,m,al,ar);//建立左子树
	build(id<<1|1,m+1,hr,al,ar);//建立右子树
}
void sub_add(int id,int rt,int a,double l)
{
	ans[id].stu[rt].m=max(ans[id].stu[rt].m,l);//储存答案
	if(ans[id].stu[rt].al==ans[id].stu[rt].ar)return;
	int m=(ans[id].stu[rt].al+ans[id].stu[rt].ar)>>1;
	if(a<=m)sub_add(id,rt<<1,a,l);
	else sub_add(id,rt<<1|1,a,l);
	ans[id].stu[rt].m=max(ans[id].stu[rt<<1].m,ans[id].stu[rt<<1|1].m);//将答案向上更新,否则全部都会等于初始化的-1.0,导致答案错误
}
void add(int id,int h,int a,double l)
{
	sub_add(id,1,a,l);//将a的值传入第二维线段树
	if(ans[id].hl==ans[id].hr)return;
	int m=(ans[id].hl+ans[id].hr)>>1;
	if(h<=m)add(id<<1,h,a,l);//判断插入左子树或右子树
	else add(id<<1|1,h,a,l);
}
double sub_query(int id,int rt,int al,int ar)
{
	if(ans[id].stu[rt].al==al&&ans[id].stu[rt].ar==ar)return ans[id].stu[rt].m;//若活泼度也符合范围,直接返回答案m
	int m=(ans[id].stu[rt].al+ans[id].stu[rt].ar)>>1;
	if(ar<=m)return sub_query(id,rt<<1,al,ar);//若要查找的范围的最大值ar比中间值m小,直接从左子树查找
	else if(al>m)return sub_query(id,rt<<1|1,al,ar);//<span style="font-family: Arial, Helvetica, sans-serif;">若要查找的范围的最小值al比中间值m大,直接从右子树查找</span>
	else return max(sub_query(id,rt<<1,al,m),sub_query(id,rt<<1|1,m+1,ar));//不然就两边同时查找,返回较大的那个
}
double query(int id,int hl,int hr,int al,int ar)
{
	if(ans[id].hl==hl&&ans[id].hr==hr)//若身高符合范围,则进入第二维线段树,查找活泼度
		return sub_query(id,1,al,ar);
	int m=(ans[id].hl+ans[id].hr)>>1;
	if(hr<=m)return query(id<<1,hl,hr,al,ar);//要查找的身高最大值hr都比当前查找范围的中间值m小,则从左子树中查找
	else if(hl>m)return query(id<<1|1,hl,hr,al,ar);//同上,要查找的最小值hl都比当前范围的中间值m大,从右子树中查找
	else return max(query(id<<1|1,m+1,hr,al,ar),query(id<<1,hl,m,al,ar));//否则同时查找,返回较大的那个
}
int main()
{
	int m;
	while(scanf("%d",&m)&&m)
	{
		build(1,100,200,0,1000);//1是线段树的根,100-200是身高范围,0-1000是活泼度,活泼度本来是0-100的浮点数,将其转化成0-1000的整形方便建树计算
		char cmd[4];
		while(m--)
		{
			scanf("%s",cmd);
			if(cmd[0]=='I')
			{
				int h;
				double a,l;
				scanf("%d%lf%lf",&h,&a,&l);
				int aa=a*10;//将活泼度转化成整数
				add(1,h,aa,l);//插入一个新的节点
			}else{
				int hl,hr;
				double al,ar;
				scanf("%d%d%lf%lf",&hl,&hr,&al,&ar);
				int aa=10*al,bb=ar*10;//同上
				if(hl>hr)//防止访问越界
				{
					int temp=hl;
					hl=hr;
					hr=temp;
				}
				if(aa>bb)//同上
				{
					int temp=aa;
					aa=bb;
					bb=temp;
				}
				double res=query(1,hl,hr,aa,bb);//查询结果
				if(res==-1.0)printf("-1\n");
				else printf("%.1lf\n",res);
			}
		}
	}
	return 0;
}


PS:依我个人之见,可以将二维线段树看成是一个矩形,建树和查找的过程都可以看成是先确定行或列,然后再在列或行里进行进一步的操作。用这种思考方式,建树和查找的思路都会非常清晰。

还有就是我上面的代码用了重名,例如都用hl和hr表示身高的左右范围,纯属个人习惯。你们看的时候要注意区分线段树里的hl,hr,al,ar和你传进去的hl,hr,al,ar。传进去的值是不能改变的,用于确定查询范围。要改变的是你查找的节点,也就是线段树里的值。为了不搞混你可以取不同的名字~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值