[Tsinsen A1482] 登頂計劃(計算幾何+單調棧)

題意

  • 有一個nn個點構成的折線,有一個人在這些折現中行走,問從每個點出發到達最高點的最小距離(n105\rm{n \le10^5})。

發現可能出現轉向的點一定是前i\rm i個點構成的上凸殼最後一條線與折線的交點,那麼這樣總點數就是O(n)O(n)級別的,我們可以利用單調棧來維護凸包同時求出所有關鍵點。

假設我們知道了所有關鍵點,那麼每個點肯定是先走到一個看到最高點比它看到的最高點高的地方,再從那個點走向終點。我們發現第ii個點向左能看到的最高的點,恰好就是前ii個點構成的凸包上的倒數第二個點,我們還是可以用一個單調棧O(n)O(n)解決這個問題。

我們已經知道了每個點能看到的最高點,那麼我們再一次用單調棧找到左右第一個比當前點能看到最高點看的高的點,並把那個點向當前點連一條邊,最後從最高點DFS一遍即可,複雜度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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章