hdu 5033 Building(单调性+二分)

题目链接:hdu 5033 Building

题目大意:城市里有n座摩天大厦,给定每栋大厦的位置和高度,假定大厦的宽度为0。现在有q次查询,表示人站的位置,人的高度视为0,问说可以仰望天空的角度。

解题思路:比赛的时候用单调性优化直接给过了,对于每个大厦来说,记录左右两边与其形成斜率最大的大厦序号以及斜率,然后每次查询是,通过二分确认人所在位置属于哪两栋大厦之间,然后分别向左向右确认角度,在确认角度时,根据大厦记录的最大斜率进行判断。

以左边为例,

然后对于查询位置x:

这种解法仅仅是优化查询效率而已,对于极端数据(斜率一直处于递减),对于单次查询复杂度就变成了o(n),虽然测试样例没有这样的数据,但貌似不是正解。

C++  优化查询

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

using namespace std;

const int maxn = 2 * 1e5 + 5;
const double pi = atan(1.0) * 4;

struct point {
    double x, h;
    bool operator < (const point& u) const {
        return x < u.x;
    }
}p[maxn];

struct edge {
    int v;
    double k;

    void set (int v, double k) {
        this->v = v;
        this->k = k;
    }
}l[maxn], r[maxn];

int N;

inline double get_k(const point& a, const point& b) {
    return (a.h - b.h) / fabs(b.x - a.x);
}

void init () {
    scanf("%d", &N);
    for (int i = 0; i < N; i++)
        scanf("%lf%lf\n", &p[i].x, &p[i].h);
    sort(p, p + N);

    l[0].set(-1, 0);
    for (int i = 1; i < N; i++) {
        int u = i - 1;

        while (l[u].v != -1 && l[u].k > get_k(p[u], p[i]))
            u = l[u].v;

        l[i].v = u;
        l[i].k = get_k(p[u], p[i]);
    }

    r[N-1].set(-1, 0);
    for (int i = N - 2; i >= 0; i--) {
        int u = i + 1;

        while (r[u].v != -1 && r[u].k > get_k(p[u], p[i]))
            u = r[u].v;

        r[i].v = u;
        r[i].k = get_k(p[u], p[i]);
    }
}

void solve () {
    int n;
    point q;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        scanf("%lf", &q.x);
        q.h = 0;
        int idx = lower_bound(p, p + N, q) - p;

        int mv = idx - 1;
        while (l[mv].v != -1 && l[mv].k > get_k(p[mv], q))
            mv = l[mv].v;

        while (r[idx].v != -1 && r[idx].k > get_k(p[idx], q))
            idx = r[idx].v;
        double R = pi - atan(get_k(p[mv], q)) + atan(get_k(q, p[idx]));
        printf("%.10lf\n", R / pi * 180);
    }
}

int main () {
    int cas;
    scanf("%d", &cas);
    for (int kcas = 1; kcas <= cas; kcas++) {
        init();
        printf("Case #%d:\n", kcas);
        solve();
    }
    return 0;
}

赛后和队友讨论了一下,觉得用离线算法应该是正解,将所有查询预先度入,然后统一按照x坐标排序,同样是以左边为例,维护斜率的单调栈,然后碰到询问点就直接在栈中二分,这样单次查询的复杂度就变成了o(logn)。

当处理到x2时,栈中存在<2>号边;当处理到x1询问时,栈中存在<3>,<4>,<5>号边,每次根据询问坐标对栈中的边进行二分。

C++ 二分

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>

using namespace std;

const int maxn = 2 * 1e5 + 5;
const double pi = atan(1.0) * 4;

struct point {
    int idx;
    double x, h;
    point (int idx = 0, double x = 0, double h = 0) {
        this->idx = idx;;
        this->x = x;
        this->h = h;
    }

    bool operator < (const point& u) const {
        return x < u.x;
    }
};

struct edge {
    int v;
    double k;

    edge (int v, double k) {
        this->v = v;
        this->k = k;
    }
};

int N;
double l[maxn], r[maxn];
vector<point> p;
vector<edge> g;

inline double get_k(const point& a, const point& b) {
    return (a.h - b.h) / fabs(b.x - a.x);
}

void init () {

    p.clear();

    double x, h;
    scanf("%d", &N);
    for (int i = 0; i < N; i++) {
        scanf("%lf%lf", &x, &h);
        p.push_back(point(0, x, h));
    }

    scanf("%d", &N);
    for (int i = 1; i <= N; i++) {
        scanf("%lf", &x);
        p.push_back(point(i, x, 0));
    }
    sort(p.begin(), p.end());
}

double bsearch (point u) {
    int L = 0, R = g.size();

    while (L < R) {
        int mid = (L + R) / 2;
        if (g[mid].k > get_k(p[g[mid].v], u))
            R = mid;
        else
            L = mid + 1;
    }

    L--;
    double k = p[g[L].v].h / fabs(p[g[L].v].x - u.x);
    return  atan(k);
}

void solve () {

    g.clear();
    g.push_back(edge(0, -1));
    for (int i = 1; i < p.size(); i++) {
        if (p[i].idx > 0) {
            l[p[i].idx] = bsearch(p[i]);
        } else {

            int u = g.size() - 1;
            while (u && g[u].k > get_k(p[g[u].v], p[i])) {
                g.pop_back();
                u--;
            }
            g.push_back(edge(i, get_k(p[g[u].v], p[i])));
        }
    }

    g.clear();
    g.push_back(edge(p.size()-1, -1));
    int u = p.size() - 1;
    for (int i = p.size() - 2; i >= 0; i--) {
        if (p[i].idx > 0) {
            r[p[i].idx] = bsearch(p[i]);
        } else {

            int u = g.size() - 1;
            while (u && g[u].k > get_k(p[g[u].v], p[i])) {
                g.pop_back();
                u--;
            }

            g.push_back(edge(i, get_k(p[g[u].v], p[i])));
        }
    }

    for (int i = 1; i <= N; i++)
        printf("%.10lf\n", (pi - l[i] - r[i]) / pi * 180);
}

int main () {
    int cas;
    scanf("%d", &cas);
    for (int kcas = 1; kcas <= cas; kcas++) {
        init();
        printf("Case #%d:\n", kcas);
        solve();
    }
    return 0;
}
时间: 2024-09-29 08:02:30

hdu 5033 Building(单调性+二分)的相关文章

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5033 解题报告:在一条x轴上有n个建筑物,每个建筑物有一个高度h,然后现在有q次查询,查询的内容是假设有一个人站在xi这个位置,问他看天空的视角是多大,用角度表示. 数据量都比较大,n和q都是10^5,但因为q次都是查询操作,并没有要求在线更新和查询,所以我们想到用离线算法,先把全部的输入接收,然后离线算出最后打出结果. 这题的思路是把所有的建筑物按照高度从大到小排序,然后所有的查询按照x从小到大排

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 (单调栈 或 暴力枚举 )

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(北京网络赛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(类凸包+向量叉积的应用)

Building Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 1138    Accepted Submission(s): 321 Special Judge Problem Description Once upon a time Matt went to a small town. The town was so sma

hdu 5033 Building(斜率优化)

Building Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 1237    Accepted Submission(s): 350 Special Judge Problem Description Once upon a time Matt went to a small town. The town was so sma

hdu 5033 Building(北京网络赛)

Building                                                            Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 1090    Accepted Submission(s): 309 Special Judge Problem Description Once

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

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