HDU 5033 Building --离线+单调栈

题意:给一些建筑物,x表示横坐标,h表示高度,然后查询某些坐标x,问从该点看向天空的最大张角是多大。

解法:离线操作,读入所有数据,然后按x升序排序,对每一个查询的x,先从左到右,依次添加x坐标小于x的建筑物,加入一个建筑物的条件:

1.此建筑物高度大于栈中的前一个,这个必然是最优的。

2.加入这个建筑物后不能使相对斜率: stk[top-2]~stk[top-1] 比a[j]~stk[top-1]大(负数),即出现凹形,否则会出现这种:

如图,即中间那个根本没用了,加入第三根的时候就要判一下。

最后算这个查询x的左角度的时候,要先判断这个点跟栈中上面两个的关系,处理一下。

然后再从右到左扫一遍,最后就得出了左右角。

之前没考虑相对斜率,而是每次加建筑物的时候,只判断与此时的查询点的绝对斜率关系,这样的bug就是会形成上图中的情况。(好蒻)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define pi (acos(-1.0))
#define eps 1e-7
using namespace std;
#define N 100007

struct Query
{
    double x;
    double Lang,Rang;
    int ind;
}ans[N];

struct node
{
    double x,h;
}a[N];
int stk[N],top;
int cmp1(Query ka,Query kb) { return ka.x < kb.x; }
int cmp2(Query ka,Query kb) { return ka.ind < kb.ind; }
int cmpB(node ka,node kb)   { return ka.x < kb.x; }

int main()
{
    int t,cs = 1,i,j;
    int n,m;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
            scanf("%lf%lf",&a[i].x,&a[i].h);
        sort(a+1,a+n+1,cmpB);
        scanf("%d",&m);
        for(i=1;i<=m;i++)
        {
            scanf("%lf",&ans[i].x);
            ans[i].ind = i;
        }
        sort(ans+1,ans+m+1,cmp1);
        double now;
        top = 0;
        j = 1;
        for(i=1;i<=m;i++)
        {
            while(j <= n && a[j].x < ans[i].x)
            {
                while(top >= 1 && a[j].h >= a[stk[top-1]].h)           //高度大于之前的,肯定更优
                    top--;
                while(top >= 2 && (a[j].h-a[stk[top-1]].h)/(fabs(a[j].x-a[stk[top-1]].x)) >= (a[stk[top-1]].h-a[stk[top-2]].h)/(fabs(a[stk[top-1]].x-a[stk[top-2]].x)))
                    top--;                                             //相对斜率递减(负数)
                stk[top++] = j;
                j++;
            }
            while(top >= 2 && a[stk[top-1]].h/(fabs(a[stk[top-1]].x-ans[i].x)) <= a[stk[top-2]].h/(fabs(a[stk[top-2]].x-ans[i].x)))
                top--;                                                 //对这个点
            now = a[stk[top-1]].h/(fabs(a[stk[top-1]].x-ans[i].x));    //最终答案为栈顶的建筑
            ans[i].Lang = 180.0*atan2(now,1.0)/pi;
        }
        top = 0;              //反着来一遍
        j = n;
        for(i=m;i>=1;i--)
        {
            while(j >= 1 && a[j].x > ans[i].x)
            {
                while(top >= 1 && a[j].h >= a[stk[top-1]].h)
                    top--;
                while(top >= 2 && (a[j].h-a[stk[top-1]].h)/(fabs(a[j].x-a[stk[top-1]].x)) >= (a[stk[top-1]].h-a[stk[top-2]].h)/(fabs(a[stk[top-1]].x-a[stk[top-2]].x)))
                    top--;
                stk[top++] = j;
                j--;
            }
            while(top >= 2 && a[stk[top-1]].h/(fabs(a[stk[top-1]].x-ans[i].x)) <= a[stk[top-2]].h/(fabs(a[stk[top-2]].x-ans[i].x)))
                top--;
            now = a[stk[top-1]].h/(fabs(a[stk[top-1]].x-ans[i].x));
            ans[i].Rang = 180.0*atan2(now,1.0)/pi;
        }
        sort(ans+1,ans+m+1,cmp2);
        printf("Case #%d:\n",cs++);
        for(i=1;i<=m;i++)
            printf("%.10f\n",180.0-ans[i].Lang-ans[i].Rang);
    }
    return 0;
}

时间: 2024-08-03 02:28:01

HDU 5033 Building --离线+单调栈的相关文章

hdu - 5033 - Building(单调栈)

题意:N 幢楼排成一列(1<=N<=10^5),各楼有横坐标 xi(1<=xi<=10^7) 以及高度 hi(1<=hi<=10^7),在各楼之间的Q个位置(1<=Q<=10^5),问这些位置可以仰望天空的夹角是多少度. 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5033 -->>将楼和人的位置一起按 x 排序.. 从左往右扫,单调栈维护斜率小的.. 从右往左扫,单调栈维护斜率大的.. #inc

HDU 5033 Building (维护单调栈)

题目链接 题意:n个建筑物,Q条询问,问所在的位置,看到天空的角度是多少,每条询问的位置左右必定是有建筑物的. 思路 : 维护一个单调栈,将所有的建筑物和所有的人都放到一起开始算就行,每加入一个人,就维护栈里的建筑物的高度,也就是说这个人所能够看到的建筑物时在栈里的,但是这个人看不到的就删掉,例如下图,中间三个点可以删掉了,因为角度问题中间三个是看不到的,所以不会影响最终求得角度 还有一种情况,就是下述的情况,不停的维护单调栈,人站在不同的地方角度也是不一样的. 维护的是相邻两建筑顶(xi,hi

hdu 5033 buiding(单调栈)

hdu 5033 buiding(单调栈) 某年某月某天,马特去了一个小镇.这个小镇如此狭窄,以至于他可以把小镇当作一个枢纽.在镇上有一些摩天大楼,其中一栋位于xi,高度为hi.所有的摩天大楼位于不同的地方.为了简化题目,假设摩天大楼没有宽度.由于摩天大楼如此之高,马特几乎看不到天空.对于马特所在的位置,他想知道他能看到天空的角度范围有多大.假设马特的身高是0.可以保证,对于每个查询,马特的左右两边至少有一座建筑物,而且他的位置上没有建筑物.建筑物的数量n<1e5,查询次数Q<1e5. 我用常

hdu 5033 Building(单调性+二分)

题目链接:hdu 5033 Building 题目大意:城市里有n座摩天大厦,给定每栋大厦的位置和高度,假定大厦的宽度为0.现在有q次查询,表示人站的位置,人的高度视为0,问说可以仰望天空的角度. 解题思路:比赛的时候用单调性优化直接给过了,对于每个大厦来说,记录左右两边与其形成斜率最大的大厦序号以及斜率,然后每次查询是,通过二分确认人所在位置属于哪两栋大厦之间,然后分别向左向右确认角度,在确认角度时,根据大厦记录的最大斜率进行判断. 以左边为例, 然后对于查询位置x: 这种解法仅仅是优化查询效

HDU 5033 Building(北京网络赛B题)

HDU 5033 Building 题目链接 思路:利用单调栈维护建筑建的斜线,保持斜率单调性,然后可以把查询当成高度为0的建筑,和建筑和在一起考虑,从左往右和从右往左各扫一遍即可 代码: #include <cstdio> #include <cstring> #include <queue> #include <cmath> #include <algorithm> using namespace std; const int N = 200

HDU 5033 Building(北京网络赛B题) 单调栈 找规律

做了三天,,,终于a了... 11724203 2014-09-25 09:37:44 Accepted 5033 781MS 7400K 4751 B G++ czy Building Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 1257    Accepted Submission(s): 358 Special Judg

hdu 5033 Building (单调栈 或 暴力枚举 )

Description Once upon a time Matt went to a small town. The town was so small and narrow that he can regard the town as a pivot. There were some skyscrapers in the town, each located at position x i with its height h i. All skyscrapers located in dif

HDU 5033 Building(2014北京网络赛 单调栈+几何)

博客原文地址:http://blog.csdn.net/xuechelingxiao/article/details/39494433 Building 题目大意:有一排建筑物坐落在一条直线上,每个建筑物都有一定的高度,给出一个X坐标,高度为0,问X位置能看到的视角是多少度.如图: 图一: 图二: 图一为样例一,图二为样例三,红色部分为高楼,蓝色虚线为视角的最大范围. 解题思路: 由于有10w个点跟10w次询问,所以朴素的算法判断肯定会超时的.所以就需要用单调栈,维护相邻两建筑顶(xi,hi)的

HDU - 5033 Building (单调栈+倍增)

题意:有一排建筑,每座建筑有一定的高度,宽度可以忽略,求在某点的平地上能看到天空的最大角度. 网上的做法基本都是离线的...其实这道题是可以在线做的. 对于向右能看到的最大角度,从右往左倍增维护每个时刻的单调栈(凸壳),对于每个询问,先二分找到它右面的第一个建筑的位置,然后在对应的凸壳上倍增找到切点即可. 向左看把x坐标对称一下就行. 复杂度$O(nlogn)$ 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long lo