本题是二维线段树模板,题目链接: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;
}
还有就是我上面的代码用了重名,例如都用hl和hr表示身高的左右范围,纯属个人习惯。你们看的时候要注意区分线段树里的hl,hr,al,ar和你传进去的hl,hr,al,ar。传进去的值是不能改变的,用于确定查询范围。要改变的是你查找的节点,也就是线段树里的值。为了不搞混你可以取不同的名字~~