题意
- 有一个 n n n个点构成的折线,有一个人在这些折现中行走,问从每个点出发到达最高点的最小距离( n ≤ 1 0 5 \rm{n \le10^5} n≤105)。
发现可能出现转向的点一定是前 i \rm i i个点构成的上凸壳最后一条线与折线的交点,那么这样总点数就是 O ( n ) O(n) O(n)级别的,我们可以利用单调栈来维护凸包同时求出所有关键点。
假设我们知道了所有关键点,那么每个点肯定是先走到一个看到最高点比它看到的最高点高的地方,再从那个点走向终点。我们发现第 i i i个点向左能看到的最高的点,恰好就是前 i i i个点构成的凸包上的倒数第二个点,我们还是可以用一个单调栈 O ( n ) O(n) O(n)解决这个问题。
我们已经知道了每个点能看到的最高点,那么我们再一次用单调栈找到左右第一个比当前点能看到最高点看的高的点,并把那个点向当前点连一条边,最后从最高点DFS一遍即可,复杂度 O ( n ) O(n) O(n)。
#include <bits/stdc++.h>
using namespace std;
typedef long double ldb;
const ldb eps = 1e-7;
const int N = 2e6 + 7;
struct Point {
ldb x, y;
ldb operator * (const Point &T) { return x * T.y - T.x * y; }
Point operator - (const Point &T) { return (Point) { x - T.x, y - T.y }; }
bool operator < (const Point &T) { return x < T.x; }
} A[N], B[N], C[N], D[N];
int m, n, bn, cn, L[N], R[N], turn[N];
void insert1(int a, int b, int c, int d) {
ldb k1 = (A[b].y - A[a].y) / (A[b].x - A[a].x), b1 = A[a].y - k1 * A[a].x;
ldb k2 = (A[d].y - A[c].y) / (A[d].x - A[c].x), b2 = A[c].y - k2 * A[c].x;
if (k1 == k2) return;
ldb x = (b2 - b1) / (k1 - k2), y = k1 * x + b1;
if (fabs(x - round(x)) > eps) B[++ bn] = (Point) { x, y };
}
void insert2(int a, int b, int c, int d) {
ldb k1 = (A[b].y - A[a].y) / (A[b].x - A[a].x), b1 = A[a].y - k1 * A[a].x;
ldb k2 = (A[d].y - A[c].y) / (A[d].x - A[c].x), b2 = A[c].y - k2 * A[c].x;
if (k1 == k2) return;
ldb x = (b2 - b1) / (k1 - k2), y = k1 * x + b1;
if (fabs(x - round(x)) > eps) C[++ cn] = (Point) { x, y };
}
int to[N], nxt[N], head[N], e;
void add(int x, int y) { to[++ e] = y, nxt[e] = head[x], head[x] = e; }
ldb ans[N], S[N], T[N];
ldb dis(Point x) { return sqrt(x.x * x.x + x.y * x.y); }
void dfs(int u) {
for (int i = head[u]; i; dfs(to[i]), i = nxt[i])
ans[to[i]] = ans[u] + abs(S[to[i]] - S[u]);
}
int main() {
int Sta[N], top;
scanf("%d", &n);
for (int i = 1; i <= n; ++ i)
scanf("%Lf%Lf", &A[i].x, &A[i].y);
Sta[top = 1] = 1;
for (int i = 2; i <= n; Sta[++ top] = i ++) {
for (; top > 1; -- top) {
ldb x = (A[Sta[top - 1]] - A[i]) * (A[Sta[top]] - A[i]);
if (x < -eps) break;
if (x > eps && Sta[top] != i - 1)
insert1(Sta[top - 1], Sta[top], i, i - 1);
}
if (top && A[Sta[top]].y < A[i].y) -- top;
}
Sta[top = 1] = n;
for (int i = n - 1; i; Sta[++ top] = i --) {
for (; top > 1; -- top) {
ldb x = (A[Sta[top - 1]] - A[i]) * (A[Sta[top]] - A[i]);
if (x > eps) break;
if (x < -eps && Sta[top] != i + 1)
insert2(Sta[top - 1], Sta[top], i, i + 1);
}
if (top && A[Sta[top]].y < A[i].y) -- top;
}
A[n + 1].x = B[bn + 1].x = C[cn + 1].x = 1e9;
reverse(C + 1, C + cn + 1);
for (int i = 1, j = 1, k = 1; ; ) {
if (i > n && j > bn && k > cn) break;
if (A[i].x <= B[j].x && A[i].x <= C[k].x)
D[++ m] = A[i ++];
else if (B[j].x <= A[i].x && B[j].x <= C[k].x)
D[++ m] = B[j ++];
else if (C[k].x <= A[i].x && C[k].x <= B[j].x)
D[++ m] = C[k ++];
}
swap(A, D);
Sta[top = 1] = 1;
for (int i = 2; i <= m; Sta[++ top] = i ++) {
while (top && A[Sta[top]].y < A[i].y) -- top;
while (top > 1 && (A[i] - A[Sta[top]]) * (A[i] - A[Sta[top - 1]]) <= eps) -- top;
L[i] = top ? Sta[top] : 0;
}
Sta[top = 1] = m;
for (int i = m - 1; i; Sta[++ top] = i --) {
while (top && A[Sta[top]].y < A[i].y) -- top;
while (top > 1 && (A[i] - A[Sta[top]]) * (A[i] - A[Sta[top - 1]]) >= -eps) -- top;
R[i] = top ? Sta[top] : 0;
}
for (int i = 1; i <= m; ++ i) {
if (i > 1) S[i] = S[i - 1] + dis(A[i] - A[i - 1]);
T[i] = max(A[L[i]].y, max(A[R[i]].y, A[i].y));
}
Sta[top = 1] = 1;
for (int i = 2; i <= m; Sta[++ top] = i ++) {
while (top && T[Sta[top]] - T[i] <= -eps) -- top;
if ((L[i] || R[i]) && A[L[i]].y - A[R[i]].y >= eps)
add(Sta[top], i);
}
Sta[top = 1] = m;
for (int i = m - 1; i; Sta[++ top] = i --) {
while (top && T[Sta[top]] - T[i] <= -eps) -- top;
if ((L[i] || R[i]) && A[L[i]].y - A[R[i]].y <= -eps)
add(Sta[top], i);
}
/*int mx = 0;
for (int i = 1; i <= m; ++ i) {
turn[i] = -1;
if (A[i].y > A[mx].y) mx = i;
T[i] = max(A[i].y, max(A[L[i]].y, A[R[i]].y));
if (T[i] == A[R[i]].y) turn[i] = 1;
if (T[i] == A[L[i]].y) turn[i] = 0;
}
top = 0;
for (int i = 1; i <= m; Sta[++ top] = i ++) {
while (top && T[Sta[top]] < T[i]) -- top;
if (top && turn[i] == 0) add(Sta[top], i);
}
top = 0;
for (int i = m; i >= 1; Sta[++ top] = i --) {
while (top && T[Sta[top]] < T[i]) -- top;
if (top && turn[i] == 1) add(Sta[top], i);
}*/
for (int i = 1; i <= m; ++ i)
if (!(L[i] || R[i]))
dfs(i);
for (int i = 1; i <= m; ++ i)
if (fabs(A[i].x - round(A[i].x)) < eps)
printf("%.6Lf\n", ans[i]);
return 0;
}