HDU 5033 Building 单调队列

【题目大意】

有n个房子在一条直线上,告诉你其坐标和高度(xi、hi)。m次询问,对于一个询问:x,你应该输出如果一个人(高度0)的坐标为x,他能看到的天空角度,保证每个人都左右至少有一个房子。

【思路】

对于一次询问的左边来说,对于所有xi < i,我们需要找到 max(hi/(x-xi))。右边同理,下面我们只分析左边。

这种题,想到单调性是不难的。我们可以按这个方向分析。如果我们把所有房子和询问排序,从左到右依次插入房子和询问。考虑怎么去维护“有效”的房子。

显然,如果是后出现的房子,高度更高,那么前面的房子永远不会是我们想找的房子了。那么我们就得到了一个递减的房子序列。但是这样的房子,可能还是很多,我们需要进一步的思考。如下图:


红色的竖线表示房子,当前插入最右边的那个房子。对于以后的询问,中间的那个房子,永远不会是令hi/(x-xi)达到最大的那个房子。为什么呢?对于区域①到④,要么左边的房子比中间的优,要么右边的房子比中间的优秀。那么,我们维护的房子,大概应该是如下情况:


因为分隔区间的单调,那么对于后面询问,我们可以考虑用二分去查找,得到令hi/(x-xi)达到最大的i。但是,可能是因为精度的问题,比赛的时候我这样写,一直WA。。。

其实,用二分是多余的。询问也是排了序的。如果当前我们询问的x在上图中的区域3,这时令hi/(x-xi)达到最大的是中间的那个房子。我们可以直接把最右边的两个房子直接删掉,因为对于以后的询问,这两个房子一定不是“有效”的。这样维护了后,队尾的房子,其实就是最优的。

至此,此题就可以做了。复杂度就是排序的复杂度n*log(n)

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF = 1000111222;
const double INFF = 1e100;
const double eps = 1e-8;
const int mod = 1000000007;
const int NN = 100010;
const int MM = 400010;
/* ****************** */

struct node
{
    double h;
    double x;
    bool operator<(const node &tt)const
    {
        return x < tt.x;
    }
}a[NN];
struct Q
{
    int l,r,id;
    double x;
    bool operator<(const Q &tt)const
    {
        return x < tt.x;
    }
}line[NN];
int q[NN];
double ans[NN];

const double pi = acos(-1.0);

int main()
{
    int cas, ee = 0;
    int i, n, m, j, j1, j2, tail;
    double t, t1, t2;
    scanf("%d", &cas);
    while (cas--)
    {
        scanf("%d",&n);
        for (i = 1; i <= n; i ++)
        {
            scanf("%lf%lf", &a[i].x, &a[i].h);
        }
        sort(a+1, a+1+n);
        scanf("%d", &m);
        for (i = 1; i <= m; i ++)
        {
            scanf("%lf", &line[i].x);
            line[i].id = i;
        }
        sort(line+1, line+1+m);

        q[tail = 1] = 1;
        j = 2;
        for (i = 1; i <= m; i ++)
        {
            while (j <= n && a[j].x < line[i].x)
            {
                while (tail > 0 && a[j].h >= a[ q[tail] ].h)
                    tail--;

                while (tail > 1)
                {
                    j2 = q[tail-1];
                    j1 = q[tail];
                    if( (a[j1].h - a[j].h) * (a[j1].x - a[j2].x) <=
                       (a[j2].h-a[j1].h) * (a[j].x - a[j1].x) )
                        tail--;
                    else
                        break;
                }
                q[++tail] = j;
                j++;
            }

            while (tail > 1)
            {
                j1 = q[tail-1];
                j2 = q[tail];
                if(a[j1].h/(line[i].x - a[j1].x) >=
                   a[j2].h/(line[i].x - a[j2].x))
                    tail--;
                else
                    break;
            }
            line[i].l = q[tail];
        }

        q[tail = 1] = n;
        j = n-1;
        for (i = m; i >= 1; i --)
        {
            while (j >= 1 && a[j].x > line[i].x)
            {
                while (tail > 0 && a[j].h >= a[ q[tail] ].h)
                    tail--;

                while (tail > 1)
                {
                    j1 = q[tail];
                    j2 = q[tail-1];
                    if( (a[j1].h - a[j].h) * (a[j2].x - a[j1].x) <=
                       (a[j2].h-a[j1].h) * (a[j1].x - a[j].x) )
                        tail--;
                    else
                        break;
                }
                q[++tail] = j;
                j--;
            }
            while (tail > 1)
            {
                j1 = q[tail-1];
                j2 = q[tail];
                if(a[j1].h/(a[j1].x - line[i].x) >=
                   a[j2].h/(a[j2].x - line[i].x))
                    tail--;
                else
                    break;
            }
            line[i].r = q[tail];
        }

        for (i = 1; i <= m; i ++)
        {
            t1 = a[ line[i].l ].h/( line[i].x - a[ line[i].l ].x + 0.0 );
            t2 = a[ line[i].r ].h/( line[i].x - a[ line[i].r ].x + 0.0 );
            t2 = -t2;
            t = pi - atan(t1) - atan(t2);
            ans[ line[i].id ] = t*180.0/pi;
        }
        printf("Case #%d:\n", ++ee);
        for (i = 1; i <= m; i ++)
        {
            printf("%.10lf\n", ans[i]);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值