题目地址:

  http://acm.hdu.edu.cn/showproblem.php?pid=5033

题意:

  一排N个房子,各有位置和高度,Q次询问,问小明再某一位置能看到的天空角度。(小明高度近似为0,保证小明和房子不重合)

必要思考:

  1.首先前边比房子 i 矮的房子一定看不见。可能被看到的房子一定是高度递减的。

  2.递减的过程中如果是下凹的递减也一定看不见,也就是需要时相邻两点的斜率是递增的。

  3.所用用单调栈来维护高度递减,相邻房子连线斜率递增的房子。

  4.离线处理,如果高度为0,就视为人,人不必入栈,栈顶元素为边界,左右个跑一次。

代码:

 

#include<bits/stdc++.h>
using namespace std;
struct node {
    double po;
    double h;
    int id;
};
double ans[100005];
const double pi=acos(-1.0);
vector<node> data;
bool cmp(const node &a,const node &b)
{
    return a.po<b.po;
}
int s[100005];
void solve()
{
    int top=0;
    for(int i=0;i<data.size();i++)
    {

        if(!top){
            s[++top]=i;
            continue;
        }
        while(top&&data[s[top]].h<=data[i].h)
            top--;
        while(top>=2&&fabs(data[s[top-1]].h-data[s[top]].h)*fabs(data[s[top]].po-data[i].po)>=fabs(data[i].h-data[s[top]].h)*fabs(data[s[top]].po-data[s[top-1]].po))
            top--;
        if(data[i].h==0)
        {
            ans[data[i].id]-=atan(data[s[top]].h/fabs(data[s[top]].po-data[i].po));
        }
        else
        s[++top]=i;
    }
}
int main()
{
    int T;scanf("%d",&T);
    int Case=0;
    while(T--)
    {
        data.clear();
        double po,h;node a;
        printf("Case #%d:\n",++Case);
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf",&a.po,&a.h);
            data.push_back(a);

        }
        int m;scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%lf",&a.po);
            a.h=0;a.id=i;
            data.push_back(a);
            ans[i]=pi;
        }
        sort(data.begin(),data.end(),cmp);
        solve();
        reverse(data.begin(),data.end());
        solve();
        for(int i=1;i<=m;i++)
        {
            printf("%.10f\n",ans[i]*180/pi);
        }
    }

}