hdu 5033 buiding(单调栈)

hdu 5033 buiding(单调栈)

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

我用常规思路,想不出什么好方法。既然这道题的普遍解法是单调栈,那么我们就来看一下单调栈怎么实现。首先,单调队列和单调栈的前提是,加入的元素满足一种有序的关系。所以我们自然而然地就想到,将所有查询排序,从左向右和从右向左分别进行查询的计算。以从左向右为例,如何做到在(均摊)O1的时间内找到一个查询的答案呢?我们先来找些性质。

性质1:如果一个建筑物i比它左侧的j高,那么当i和j都在马特左侧时,马特一定看不到j。也就是说,如果维护一个关于建筑物下标的单调栈,新加进来一个建筑时,可以把它左侧所有更矮的建筑删掉。那么,现在的单调栈就是单调递减的。这就去除了第一种冗余状态。

性质2:从左向右而言,如果有这样的情况:

,也就是单调栈中的顶上两个建筑和新加进来的建筑组成一个凹包(不好意思,下凸包),那么可以直接把单调栈顶的建筑删了,一直循环删下去。因为无论马特怎么站,都不会被它挡住。第二种冗余状态也被我们去除了。那么现在,单调栈存储的建筑就组成了一个上凸包。

性质3:然而,优化力度还不够大。这样子算法依然不是O(n)的。那怎么办呢?

由于马特一直往右跑,那么他与凸包相切的点只会往左移动:

所以,在加入新的建筑之后,判断一下马特与当前凸包的交点,然后将交点右侧的建筑全删了(弹出)即可。栈顶的建筑就是最优建筑(切线嘛)。这就去除了第三种冗余状态。然而这样的时间复杂度是均摊O(1)的吗?额,显然的。

这可能是目前为止我写的最详细的一篇博文。。

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=1e5+5;
const double pi=3.1415926535898;
struct stack{
    int t, a[maxn];
    void push(int x){ a[++t]=x; }
    void pop(){ if (--t==-1) ++t; }
    void reset(){ t=0; }
    inline int top(){ return a[t]; }
    inline int top2(){ return a[t-1]; }
}s;
struct query{
    int id, ans1, ans2; double x;
}q[maxn], q2[maxn];
struct building{
    double x, h;
}b[maxn];

int n, T, Q;

bool cmp1(query &a, query &b){
    return a.x<b.x; }
bool cmp2(query &a, query &b){
    return a.id<b.id; }
bool cmpb(building &a, building &b){
    return a.x<b.x; }

double angle(double x, double y){
    return atan(x/y)*180/pi;
}

int main(){
    scanf("%d", &T); int tmp=0;
    while (T--){
        ++tmp; s.reset();
        scanf("%d", &n);
        for (int i=1; i<=n; ++i)
            scanf("%lf%lf", &b[i].x, &b[i].h);
        scanf("%d", &Q);
        for (int i=1; i<=Q; ++i){
            scanf("%lf", &q[i].x);
            q[i].id=i;
        }
        sort(b+1, b+n+1, cmpb);
        sort(q+1, q+Q+1, cmp1);
        int nowbui=1; s.push(1);
        for (int i=1; i<=Q; ++i){
            while (b[nowbui+1].x<q[i].x){ //从左到右加入建筑物
                ++nowbui;
                //第一种冗余状态:左边的楼比右边的矮
                while (s.t>0&&b[s.top()].h<=b[nowbui].h) s.pop();
                //所以现在这个建筑物序列严格下降
                //第二种冗余状态:是个下凸函数
                while (s.t>1&&(b[nowbui].h-b[s.top()].h)/
                       (b[nowbui].x-b[s.top()].x)>=
                       (b[s.top()].h-b[s.top2()].h)/
                       (b[s.top()].x-b[s.top2()].x)) s.pop();
                //所以现在这个建筑物序列是凸包了
                s.push(nowbui);
            }
            //第三种冗余状态:若一栋楼在视线所切的那栋楼的右边,则可以删去
            //可以用均摊,证明这里的复杂度是常数
            while (s.t>1&&(b[s.top2()].h)/(q[i].x-b[s.top2()].x)>
                   (b[s.top()].h)/(q[i].x-b[s.top()].x)) s.pop();
            //现在终于不仅排除了冗余状态,并且找到了最优解。
            q[i].ans1=s.top();
        } //从右往左也一样
        nowbui=n; s.reset(); s.push(n);
        for (int i=Q; i>=1; --i){
            while (b[nowbui-1].x>q[i].x){
                --nowbui;
                //右边的楼比左边的矮
                while (s.t>0&&b[s.top()].h<=b[nowbui].h) s.pop();
                //下面有图 (其实这里封装一个关于斜率的函数会更好)
                while (s.t>1&&(b[nowbui].h-b[s.top()].h)/
                       (b[nowbui].x-b[s.top()].x)<=
                       (b[s.top()].h-b[s.top2()].h)/
                       (b[s.top()].x-b[s.top2()].x)) s.pop();
                s.push(nowbui);
            }
            //下面有图
            while (s.t>1&&b[s.top2()].h/(b[s.top2()].x-q[i].x)>
                   b[s.top()].h/(b[s.top()].x-q[i].x)) s.pop();
            q[i].ans2=s.top();
        }
        sort(q+1, q+Q+1, cmp2);
        printf("Case #%d:\n", tmp);
        for (int i=1; i<=Q; ++i){
            printf("%.5lf\n", 180-angle(b[q[i].ans1].h,
                q[i].x-b[q[i].ans1].x)-angle(b[q[i].ans2].h,
                    b[q[i].ans2].x-q[i].x));
        }
    }
   // printf("%.6lf", angle(1, 1));
    return 0;
}

原文地址:https://www.cnblogs.com/MyNameIsPc/p/8439957.html

时间: 2024-08-02 09:56:52

hdu 5033 buiding(单调栈)的相关文章

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 (单调栈+倍增)

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

Hdu 4923(单调栈)

题目链接 Room and Moor Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 842    Accepted Submission(s): 250 Problem Description PM Room defines a sequence A = {A1, A2,..., AN}, each of which is eit

Largest Rectangle in a Histogram HDU - 1506 (单调栈)

A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the

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(北京网络赛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 单调栈 ****

看出来是单调栈维护斜率,但是不会写,2333,原来是和询问放在一起的 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <queue> 6 #include <cmath> 7 typedef __int64 ll; 8 using namespace std; 9 10 const doub

HDU 5033 Building (维护单调栈)

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

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

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