您的位置:首页 > 产品设计 > UI/UE

HDU 5033 Building(单调栈, 类凸包)

2014-11-15 21:47 459 查看


Building

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)

Total Submission(s): 1661    Accepted Submission(s): 488
Special Judge


Problem 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 xi with its height hi. All skyscrapers located in different
place. The skyscrapers had no width, to make it simple. As the skyscrapers were so high, Matt could hardly see the sky.Given the position Matt was at, he wanted to know how large the angle range was where he could see the sky. Assume that Matt's height is
0. It's guaranteed that for each query, there is at least one building on both Matt's left and right, and no building locate at his position.

 

Input

The first line of the input contains an integer T, denoting the number of testcases. Then T test cases follow.

Each test case begins with a number N(1<=N<=10^5), the number of buildings.

In the following N lines, each line contains two numbers, xi(1<=xi<=10^7) and hi(1<=hi<=10^7).

After that, there's a number Q(1<=Q<=10^5) for the number of queries.

In the following Q lines, each line contains one number qi, which is the position Matt was at.

 

Output

For each test case, first output one line "Case #x:", where x is the case number (starting from 1).

Then for each query, you should output the angle range Matt could see the sky in degrees. The relative error of the answer should be no more than 10^(-4).

 

Sample Input

3
3
1 2
2 1
5 1
1
4
3
1 3
2 2
5 1
1
4
3
1 4
2 3
5 1
1
4

 

Sample Output

Case #1:
101.3099324740
Case #2:
90.0000000000
Case #3:
78.6900675260

 

Source

2014 ACM/ICPC Asia Regional Beijing Online

 

题目大意:

地面上有n座楼,分别站在m个位置上,问每个位置能看见多大角度的天空。

解题思路:

顶层模型:凸包应用。

总体思路:排序后,从左到右再从右到左,分两次得出每个查询点的左侧和右侧天空角度,相加即是答案。

详细分析:

通过画图分析,可以得出以下两点:



1. 如上图所示,在一座高楼左侧的矮楼,对于高楼右侧的任意位置,计算天空角度时都是无关的。



2. 如上图所示,中间的紫色楼,对于第四座楼右侧的位置,计算角度时也是无关的。右侧位置的左仰角都在红线和黄线之间。

所以,这题可以用类似求凸包的方法,利用单调栈进行维护。计算左侧天空角度时,左左向右扫描,始终维护当前站立位置左侧得上凸包,因为不在凸包上的点(将楼房抽象为顶部代表的点),对于当前及之后的角度计算都是无关的,可以直接去掉。

对于一个位置,其左侧天空角度,就是沿着下凸包走(即维护上凸包的过程),停止时其与栈首的第一座房子的夹角。

以上分析和解题过程,对右侧情况同样适用,只需对坐标反转即可。两侧角度相加就是整个天空的角度。

解题技巧:扫描时,可以将楼房和询问位置统一处理,减少代码量。

参考代码:

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

const int MAXN = 200010;
const double PI = acos(-1);
double ans[MAXN >> 1];
int nCase, cCase, n, q;

struct Building {
int x, h, id;
bool operator < (const Building &t) const {
return x < t.x;
}
} building[MAXN], st[MAXN];

void input() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d%d", &building[i].x, &building[i].h);
}
scanf("%d", &q);
for (int i = 0; i < q; i++) {
scanf("%d", &building[n+i].x);
building[n+i].id = i;
building[n+i].h = 0;
ans[i] = 0;
}
}

double angle(const Building &a, const Building &b) {
return atan((double)a.h / (double)(b.x - a.x));
}

bool judge(const Building &a, const Building &b, const Building &c) {
return (long long)(c.x - a.x) * (c.h - b.h) - (long long)(c.x - b.x) * (c.h - a.h) >= 0;
}

void calc() {
int head = 0;
for (int i = 0; i < n+q; i++) {
if (building[i].h > 0) {  // building
//while (head-1 >= 0 && st[head-1].h <= building[i].h) head--;
while (head-2 >= 0 && judge(st[head-2], st[head-1], building[i])) head--;
st[head++] = building[i];
} else {  // position
while (head-2 >= 0 && judge(st[head-2], st[head-1], building[i])) head--;
ans[building[i].id] += angle(st[head-1], building[i]);
}
}
}

void solve() {
// left
sort(building, building+n+q);
calc();
// right
reverse(building, building+n+q);
for (int i = 0; i < n+q; i++) {
building[i].x = 1e7 - building[i].x;
}
calc();

printf("Case #%d:\n", ++cCase);
for (int i = 0; i < q; i++) {
printf("%.10lf\n", (PI - ans[i]) / PI * 180.0);
}
}

int main() {
scanf("%d", &nCase);
while (nCase--) {
input();
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm-icpc 单调栈 凸包