题意:在一条直线上,给定一系列摩天大楼的位置和高度,每次询问自己所在位置所能看到的视野。
分析:关键在于楼与楼之间,到当前将要加进去的楼或人的位置,若之前的楼比将要进来的楼矮,可以忽略,因为对后面有影响的,只有比将要加进去的楼高的楼,最终构成递减序列,这是最基本的要求。对于从左边开始连续的三幢楼a,b,c,其中以c为基点,若a与c构成的仰角>=b与c构成的仰角,则b对于后面更远的楼层或人的位置将不起作用,故b可以忽略此时。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
struct node
{
double x,h;
int id;
bool operator<(const node &p)const
{
return x<p.x;
}
};
const int MAX=100005;
const double PI=acos(-1.0),EPS=1e-8;
node p[MAX<<1],st[MAX<<1];
double ans[MAX];
int n,q;
bool is_out(node &a,node &b,node &c)
{
return (a.h-c.h)*(c.x-b.x)>=(b.h-c.h)*(c.x-a.x);//以c为基点,比较a,b两幢楼的倾斜角的大小,若b较小或等于,说明b被覆盖,出栈!
}
double getAngle(node & a,node &b)
{
double an=90.0-180.0*atan((double)a.h/(double)(b.x-a.x))/PI;//返回人所在位置为b时,与左边楼a的连线与竖直方向构成的倾斜角大小!
//printf("%d %d %lf\n",a.h,b.x-a.x,an);
return an;
}
/*
实时维护单调递减的栈,即楼高要递减,且一定包含到目前为止将要入栈的楼,不满足的出栈,同时若栈顶所在楼被覆盖,则不断出栈!
直到栈顶所在楼是目前为止视线最窄的。
*/
void getAns()
{
int top=0;
for(int i=0; i<n+q; i++)
{
//printf("current %d %d\n",p[i].x,p[i].h);
while(top>=1&&st[top-1].h<=p[i].h)
{
top--;
//printf("%d %d 高度低\n",st[top-1].x,st[top-1].h);
}
while(top>=2&&is_out(st[top-2],st[top-1],p[i]))
{
top--;
//printf("%d %d 倾斜角低\n",st[top-1].x,st[top-1].h);
}
if(p[i].h<EPS)
ans[p[i].id]+=getAngle(st[top-1],p[i]);
else
st[top++]=p[i];
//printf("end\n");
}
}
int main()
{
int t,i,cas=1;
//freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=0; i<n; i++)
scanf("%lf%lf",&p[i].x,&p[i].h);
scanf("%d",&q);
for(i=0; i<q; i++)
{
scanf("%lf",&p[i+n].x);
p[i+n].h=0;
p[i+n].id=i;//id保存的是人的位置。
}
sort(p,p+n+q);
memset(ans,0,sizeof(ans));
getAns();//位置x递增求左边视线角。
reverse(p,p+n+q);
for(i=0; i<n+q; i++)
p[i].x=10000005.0-p[i].x;//原来位置右边x最大,现在变最小,求右边视线角!
getAns();
printf("Case #%d:\n",cas++);
for(i=0; i<q; i++)
printf("%.10lf\n",ans[i]);
}
return 0;
}