题意
一条直线上有很多房子,房子的信息包括位置和高度,给出一些询问,当有一个人站在某两个房子之间时,能看到的最大的角度(尽可能广阔的天空)
求解
可以很容易想到求解左边最大的仰角,右边最大的仰角,然后用180度减去两者,所以我们对查询排序,离线依次处理从左到右每个查询的向左看最大仰角,从右向左处理每个查询的向右看最大仰角,离线处理的目的是利用已经处理过的查询的信息,从而降低复杂度,首先,对于一个查询,显然最大仰角只会出现在高度递减的楼中的某一个,其次,一旦求出对应某个最大仰角的楼,其他比这栋楼低的楼不可能在下一次查询的解空间里,因此我们只需要单调栈维护一下递减的楼序列,找出最大值后去掉一些对下次查询不可能的值即可
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100050;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
struct Building {
int pos;
double height;
bool operator < (const Building &t)const {
return pos < t.pos;
}
};
struct Person {
int pos,id;
double Ltan,Rtan;
bool operator < (const Person &t)const {
return pos < t.pos;
}
};
Building building[maxn];
Person person[maxn];
int N,Q,mystack[maxn];
double ans[maxn];
double calcu(int i,int j)//第i个人到第j个房子的tan值
{
return building[j].height/abs(building[j].pos - person[i].pos);
}
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
for(int i=0;i<N;i++) {
scanf("%d%lf",&building[i].pos,&building[i].height);
}
sort(building,building+N);
scanf("%d",&Q);
for(int i=0;i<Q;i++) {
scanf("%d",&person[i].pos);
person[i].id = i;
person[i].Ltan = person[i].Rtan = -1;
}
sort(person,person+Q);
int left = 0;
int cur = 0;
for(int i=0;i<Q;i++) //从左到右处理
{
for(;left < N && building[left].pos < person[i].pos ; left++)//单调栈维护一下
{
while(cur > 0 && building[left].height >= building[mystack[cur-1]].height)
cur--;
mystack[cur++] = left;
}
int maxid = -1,k=0;
for(;k<cur;k++)
{
double x = calcu(i,mystack[k]);
if(x>person[i].Ltan)
{
person[i].Ltan = x;
maxid = k;
}
}
cur = maxid + 1; //比当前楼低的楼不可能出现在以后的楼的解空间里
}
int right = N-1;
cur = 0;
for(int i=Q-1;i>=0;i--) // 从右到左处理,单调栈维护
{
for(;right >=0 && building[right].pos > person[i].pos ; right--)
{
while(cur > 0 && building[right].height >= building[mystack[cur-1]].height)
cur--;
mystack[cur++] = right;
}
int maxid = -1,k=0;
for(;k<cur;k++)
{
double x = calcu(i,mystack[k]);
if(x>person[i].Rtan)
{
person[i].Rtan = x;
maxid = k;
}
}
cur = maxid + 1;
}
memset(ans,0,sizeof(ans));
for(int i=0;i<Q;i++) //求解弧度
{
ans[person[i].id] = PI - atan(person[i].Ltan)- atan(person[i].Rtan);
}
printf("Case #%d:\n",cas++);
for(int i=0;i<Q;i++)
{
printf("%lf\n",ans[i]*180.0/PI);
}
}
return 0;
}