HDU 5033 Building (单调栈)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5033


题意:给你n栋楼,每栋楼有高度和坐标,现在给你q个询问,问你人站在某个位置,能看到的天空的最大角度


思路:将询问的人和楼组合在一起,排序后,维护单调栈使得图形为凸的,然后每次查询到人的位置的时候,都维护单调栈,那么栈首和这个人就能构成答案了,还有是楼的时候也要维护这个栈,那么前后各遍历一次,就能通过数学正切角求解了


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>

typedef long long ll;
using namespace std;

const double PI = acos(-1.0);
const int maxn = 200100;
const int inf = 1e8;

struct Node
{
    int x, h;
    bool operator <(const Node &a)const
    {
        return x < a.x;
    }
} node[maxn], stk[maxn];

double ans[maxn];
int n, q;

int check(Node a, Node b, Node c)
{
    if (c.h <= 0)
        c.h = 0;
    return (ll)(a.h - c.h) * (c.x - b.x) >= (ll)(b.h - c.h) * (c.x - a.x);
}

double ang(Node a, Node b)
{
    return atan(1.0 * (b.x - a.x) / a.h);
}

void solve()
{
    int head = 0;
    for (int i = 0; i < n + q; i++)
    {
        if (node[i].h <= 0)
        {
            while (head >= 2 && check(stk[head - 2], stk[head - 1], node[i]))
                head--;
            ans[-node[i].h] += ang(stk[head - 1], node[i]);
        }
        else
        {
            while (head && stk[head - 1].h <= node[i].h)
                head--;
            while (head >= 2 && check(stk[head - 2], stk[head - 1], node[i]))
                head--;
            stk[head++] = node[i];
        }
    }
}

int main()
{
    int t, cas = 1;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        for (int i = 0; i < n; i++)
            scanf("%d%d", &node[i].x, &node[i].h);
        scanf("%d", &q);
        for (int i = 0; i < q; i++)
        {
            scanf("%d", &node[i + n].x);
            node[i + n].h = -i;
        }
        memset(ans, 0, sizeof(ans));
        sort(node, node + n + q);
        solve();
        reverse(node, node + n + q);
        for (int i = 0; i < n + q; i++)
            node[i].x = inf - node[i].x;
        solve();
        printf("Case #%d:\n", cas++);
        for (int i = 0; i < q; i++)
            printf("%.10lf\n", ans[i] * 180.0 / PI);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值