博客原文地址:https://i-blog.csdnimg.cn/blog_migrate/68d3eb607c8f15cb774afd75c89201eb.png
题目大意:有一排建筑物坐落在一条直线上,每个建筑物都有一定的高度,给出一个X坐标,高度为0,问X位置能看到的视角是多少度。如图:
图一:
图二:
图一为样例一,图二为样例三,红色部分为高楼,蓝色虚线为视角的最大范围。
解题思路:
由于有10w个点跟10w次询问,所以朴素的算法判断肯定会超时的。所以就需要用单调栈,维护相邻两建筑顶(xi,hi)的连线的斜率的绝对值上升的单调栈。
如图的单调栈存的大致走向
这样就可以做到O(n+m)的复杂度,再加上一个排序,也只有O((n+m)*log(n+m))的复杂度,就是完全可以接受的了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 200005;
const double pi = acos(-1.0);
struct Build{
Build() {
memset(ang, 0, sizeof(ang));
}
double x, h;
double ang[2];
bool pos;
int id;
}b[MAXN], q[MAXN];
int t, n, m;
bool cmp1(Build a, Build b) {
return a.x < b.x;
}
bool cmp2(Build a, Build b) {
return a.id < b.id;
}
double deal(Build a, Build b) {
double dx = fabs(b.x - a.x);
double dy = b.h - a.h;
return dy / dx;
}
int main() {
int cas, t = 1;
scanf("%d", &cas);
while (cas--) {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%lf%lf", &b[i].x, &b[i].h);
b[i].pos = false;
b[i].id = i;
}
scanf("%d", &m);
for (int i = n; i < n + m; i++) {
scanf("%lf", &b[i].x);
b[i].h = 0;
b[i].pos = true;
b[i].id = i;
}
n += m;
sort(b, b + n, cmp1);
q[0] = b[0];
int top = 0;
for (int i = 1; i < n; i++) {
if (b[i].pos == false) {
while (top && deal(b[i], q[top]) < deal(q[top], q[top - 1]))
top--;
q[++top] = b[i];
}
else {
int tmp = top;
while (tmp && deal(b[i], q[tmp]) < deal(b[i], q[tmp - 1]))
tmp--;
b[i].ang[0] = deal(b[i], q[tmp]);
}
}
q[0] = b[n - 1];
top = 0;
for (int i = n - 2; i >= 0; i--) {
if (b[i].pos == false) {
while (top && deal(b[i], q[top]) < deal(q[top], q[top - 1]))
top--;
q[++top] = b[i];
}
else {
int tmp = top;
while (tmp && deal(b[i], q[tmp]) < deal(b[i], q[tmp - 1]))
tmp--;
b[i].ang[1] = deal(b[i], q[tmp]);
}
}
printf("Case #%d:\n", t++);
sort(b, b + n, cmp2);
for (int i = 0; i < n; i++) {
if (b[i].pos == true) {
double ans = pi - atan(b[i].ang[0]) - atan(b[i].ang[1]);
ans = ans / pi * 180;
printf("%.4lf\n", ans);
}
}
}
return 0;
}