hdu5033 Building 单调队列

//    hdu5033 Building 单调队列
//
//    题目大意:
//
//    n栋大楼,有一个高度h和位置x,现在有一个人高度为0,有q个询问
//    每个询问有一个位置x,求在位置x能看到天空的最大的角度。
//
//    解题思路:
//
//    首先得想到将q个询问的位置作为一栋大楼放在整个大楼中考虑,这样
//    问题就比较一致,也比较容易处理啦。
//
//    想象一下,每次从左往右走,对于当前位置的左半边的90度内,看天的角度是
//    逐渐减小的,这样,如果当前位置的左边比当前位置要低,那么当前位置就挡住了
//    之前的更矮的,也就是说之后的位置是不可能看到比当前更矮的大楼了,这样,就可
//    以看成是一个单调的序列,序列中元素高度单调递减,即所谓的单调队列。但仅仅只是
//    这样是不够的,也会存在比当前元素(设为h)高设为h1,在队列中h1前面的元素是h2,虽然
//    h1是比h大,但是如果顺着h1的高度看过去,会被h2挡住,这时h1是没有作用的,也要将
//    这个元素从队列中移除。
//
//    当遇到的大楼要查询的位置的时候,此时队列最后的一个元素并不一定是最优的值,因为
//    这样的值可能是不合法的,比如上面的h1会被h2挡住,并不会看到天空。照样移除这样的元素
//    最后,队列的最后一个元素就是解。
//
//    右边的问题完全可以转化为左边的问题,只是从右往左处理而已。
//
//
//    感悟:
//
//    这道题是14年北京区的网赛的一道题目,当时的我虽然看懂了题目的意思,但是真的完全不会做。
//    直到现在伟大的MW大咖,说是单调队列可以做,我就做了,但是卡了一天半,还是想不出来具体怎么解
//    首先将询问的位置当作大楼这一点我想到了,维持高度递减的单调队列我想到了,就是最后一种情况没有
//    想到,更不知道要怎么处理,在伟大的MW大咖的敦敦教诲还有耐心的提示下,终于终于想到了解决的办法
//    不容易啊不容易。在此真诚感谢MW大咖~
//
//    过程无疑会有很多的疑惑,一个地方不慎,满盘皆输,找到一处的错误,心里十分的欣喜,特别是在ac
//    之后,那种心情,实在是难以描述,感觉到自己的付出,真的是有回报,以前不懂的东西,自己认真学
//    总会有收获,哪怕收获是那么一点点,微不足道,但收获就是收获,没有这个能让人更加欣喜啦,痛苦并快乐着
//    十分的享受,还是那句话,继续练吧~~~




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

const int MAX_N = 2e5 + 9;

const double PI = acos(-1.0);
int n,q;
double deqh[MAX_N];
double deqx[MAX_N];
double angle[MAX_N];

struct node {
    double pos;
    double h;
    int id;

    node(){

    }

    node(double pos,double h,int id):pos(pos),h(h),id(id){

    }
};

node sky[MAX_N];

bool cmp(node a,node b){
    return a.pos < b.pos;
}

void input(){
    scanf("%d",&n);
    double x,h;
    for (int i = 1;i <= n;i++){
        scanf("%lf%lf",&x,&h);
        sky[i] = node(x,h,0);
    }
    scanf("%d",&q);

    for (int i = 1;i <= q;i++){
        scanf("%lf",&x);
        sky[i+n] = node(x,0.0,i);
        angle[i] = 0.0;
    }

    n += q;

    sort(sky+1,sky+n+1,cmp);
}

double getk(double a,double b){
    return fabs(a/b);
}

void getleft(){
    int head,tail;
    head = tail = 0;
    for (int i=1;i<=n;i++){
        if (sky[i].id){
            while(head + 1 < tail){
                double k1 = getk(deqh[tail-1],deqx[tail-1]-sky[i].pos);
                double k2 = getk(deqh[tail-2],deqx[tail-2]-sky[i].pos);
                if (k1 <= k2)
                    tail--;
                else break;
            }
            angle[sky[i].id] += atan(deqh[tail-1]/fabs(deqx[tail-1]-sky[i].pos));
        }else {

            while(head < tail && deqh[tail-1] < sky[i].h)
                tail--;

            while(head + 1 < tail){
                double k1 = getk(deqh[tail-1]-sky[i].h,deqx[tail-1]-sky[i].pos);
                double k2 = getk(deqh[tail-2]-sky[i].h,deqx[tail-2]-sky[i].pos);
                if (k1 <= k2)
                    tail--;
                else
                    break;
            }
            deqh[tail] = sky[i].h;
            deqx[tail++] = sky[i].pos;
        }
    }
}

void getright(){
    int head,tail;
    head = tail = 0;
    for (int i = n;i >= 1;i--){
        if (sky[i].id){
            while(head + 1 < tail){
                double k1 = getk(deqh[tail-1],deqx[tail-1]-sky[i].pos);
                double k2 = getk(deqh[tail-2],deqx[tail-2]-sky[i].pos);
                if (k1 <= k2)
                    tail--;
                else
                    break;
            }

            angle[sky[i].id] += atan(deqh[tail-1]/fabs(deqx[tail-1]-sky[i].pos));


        }else {
            while(head < tail && deqh[tail-1] < sky[i].h)
                tail--;

            while(head + 1 < tail){

                double k1 = getk(deqh[tail-1]-sky[i].h,deqx[tail-1] - sky[i].pos);
                double k2 = getk(deqh[tail-2]-sky[i].h,deqx[tail-2] - sky[i].pos);

                if (k1 <= k2){
                    tail--;
                }else
                    break;

            }
            deqh[tail] = sky[i].h;
            deqx[tail++] = sky[i].pos;
        }
    }
}

void solve(){
    getleft();
    getright();
    for (int i=1;i<=q;i++){
        printf("%.10lf\n",(PI - angle[i]) * 180.0 / PI);
    }
}


int main(){
    int t;
    //freopen("1.txt","r",stdin);
    scanf("%d",&t);
    int kase = 1;
    while(t--){
        input();
        printf("Case #%d:\n",kase++);
        solve();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值