題意
- 有一個個點構成的折線,有一個人在這些折現中行走,問從每個點出發到達最高點的最小距離()。
發現可能出現轉向的點一定是前個點構成的上凸殼最後一條線與折線的交點,那麼這樣總點數就是級別的,我們可以利用單調棧來維護凸包同時求出所有關鍵點。
假設我們知道了所有關鍵點,那麼每個點肯定是先走到一個看到最高點比它看到的最高點高的地方,再從那個點走向終點。我們發現第個點向左能看到的最高的點,恰好就是前個點構成的凸包上的倒數第二個點,我們還是可以用一個單調棧解決這個問題。
我們已經知道了每個點能看到的最高點,那麼我們再一次用單調棧找到左右第一個比當前點能看到最高點看的高的點,並把那個點向當前點連一條邊,最後從最高點DFS一遍即可,複雜度。
#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;
}