この記事は帰ってきた AOJ-ICPC Advent Calendar 2022 22 日目の記事です。
https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2154
解法
与えられた多角形の内部を 、頂点を反時計回りに とします。また、 であるとします。
辺 からのマンハッタン距離が 以下である点の集合を とすると、島のある点が浸食される条件は、その点がある に対し に含まれることです。よって、浸食された後の島は となります。
領域 は四角形もしくは六角形になります。具体的には、点 があるとき、線分 からのマンハッタン距離が 以下である領域は以下のように得られます。
- と仮定しても一般性を失わないので、そのように仮定する。
- の場合、 点 を頂点とする六角形である。
- そうでない場合、 点 を頂点とする六角形である。
Slab Decomposition で平面を の辺で分割し、それぞれの領域が浸食された後の島であるかどうかを調べます。ビット演算を用いて、それぞれの領域がどれに含まれるかを計算すると得られます。その後、島である領域と島でない領域が隣接する部分それぞれについて、長さを足し合わせればよいです。
実装
#include <bits/stdc++.h> using namespace std; const double eps = 0.000001; int sign(double x){ if (x > eps){ return 1; } else if (x < -eps){ return -1; } else { return 0; } } struct point{ double x, y; point(){ } point(double x, double y): x(x), y(y){ } point operator +(point P){ return point(x + P.x, y + P.y); } point operator -(point P){ return point(x - P.x, y - P.y); } point operator *(double k){ return point(x * k, y * k); } point operator /(double k){ return point(x / k, y / k); } }; double abs(point P){ return sqrt(P.x * P.x + P.y * P.y); } double dist(point P, point Q){ return abs(Q - P); } point rotate(point P, double t){ return point(P.x * cos(t) - P.y * sin(t), P.x * sin(t) + P.y * cos(t)); } double cross(point P, point Q){ return P.x * Q.y - P.y * Q.x; } struct line{ point A, B; line(){ } line(point A, point B): A(A), B(B){ } }; point vec(line L){ return L.B - L.A; } bool is_parallel(line L1, line L2){ return abs(cross(vec(L1), vec(L2))) < eps; } bool segment_line_intersect(line L1, line L2){ return sign(cross(L1.A - L2.A, vec(L2))) * sign(cross(L1.B - L2.A, vec(L2))) <= 0; } bool segment_intersect(line L1, line L2){ return segment_line_intersect(L1, L2) && segment_line_intersect(L2, L1); } point line_intersection(line L1, line L2){ return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2)); } vector<double> unique_eps(vector<double> A){ int N = A.size(); sort(A.begin(), A.end()); vector<double> A2; A2.push_back(A[0]); for (int i = 1; i < N; i++){ if (A[i] - A[i - 1] > eps){ A2.push_back(A[i]); } } return A2; } template <typename T> struct slab_decomposition{ function<T(T, T)> f; T e; vector<pair<line, T>> S; vector<double> X; int N; vector<vector<double>> Y; vector<int> P; vector<vector<tuple<int, int, T>>> E; slab_decomposition(function<T(T, T)> f, T e): f(f), e(e){ } void add_segment(line s, T x){ S.push_back(make_pair(s, x)); } vector<vector<pair<T, int>>> build(){ int M = S.size(); for (int i = 0; i < M; i++){ if (S[i].first.A.x > S[i].first.B.x){ swap(S[i].first.A, S[i].first.B); } } for (int i = 0; i < M; i++){ X.push_back(S[i].first.A.x); X.push_back(S[i].first.B.x); } for (int i = 0; i < M; i++){ for (int j = i + 1; j < M; j++){ if (!is_parallel(S[i].first, S[j].first) && segment_intersect(S[i].first, S[j].first)){ X.push_back(line_intersection(S[i].first, S[j].first).x); } } } X = unique_eps(X); N = X.size(); vector<int> L(M), R(M); for (int i = 0; i < M; i++){ L[i] = lower_bound(X.begin(), X.end(), S[i].first.A.x - eps) - X.begin(); R[i] = lower_bound(X.begin(), X.end(), S[i].first.B.x - eps) - X.begin(); } vector<vector<double>> A(M, vector<double>(N)); Y.resize(N); for (int i = 0; i < M; i++){ for (int j = L[i]; j <= R[i]; j++){ A[i][j] = line_intersection(S[i].first, line(point(X[j], 0), point(X[j], 1))).y; Y[j].push_back(A[i][j]); } } for (int i = 0; i < N; i++){ Y[i] = unique_eps(Y[i]); } vector<vector<int>> B(M, vector<int>(N)); for (int i = 0; i < M; i++){ for (int j = L[i]; j <= R[i]; j++){ B[i][j] = lower_bound(Y[j].begin(), Y[j].end(), A[i][j] - eps) - Y[j].begin(); } } E.resize(N - 1); for (int i = 0; i < N - 1; i++){ E[i].push_back(make_tuple(-1, -1, e)); } for (int i = 0; i < M; i++){ for (int j = L[i]; j < R[i]; j++){ E[j].push_back(make_tuple(B[i][j], B[i][j + 1], S[i].second)); } } for (int i = 0; i < N - 1; i++){ E[i].push_back(make_tuple(Y[i].size(), Y[i + 1].size(), e)); } for (int i = 0; i < N - 1; i++){ sort(E[i].begin(), E[i].end()); if (!E[i].empty()){ vector<tuple<int, int, T>> E2; int cnt = E[i].size(); E2.push_back(E[i][0]); for (int j = 1; j < cnt; j++){ if (get<0>(E[i][j - 1]) == get<0>(E[i][j]) && get<1>(E[i][j - 1]) == get<1>(E[i][j])){ get<2>(E2.back()) = f(get<2>(E2.back()), get<2>(E[i][j])); } else { E2.push_back(E[i][j]); } } E[i] = E2; } } P.resize(N + 2); P[0] = 0; P[1] = 1; for (int i = 0; i < N - 1; i++){ P[i + 2] = P[i + 1] + E[i].size() - 1; } P[N + 1] = P[N] + 1; vector<vector<int>> idL(N), idR(N); for (int i = 0; i < N; i++){ idL[i] = vector<int>(Y[i].size() + 1); idR[i] = vector<int>(Y[i].size() + 1); } idL[0] = vector<int>(Y[0].size() + 1, 0); for (int i = 0; i < N - 1; i++){ int cnt = E[i].size(); for (int j = 0; j < cnt - 1; j++){ for (int k = get<0>(E[i][j]) + 1; k <= get<0>(E[i][j + 1]); k++){ idR[i][k] = P[i + 1] + j; } for (int k = get<1>(E[i][j]) + 1; k <= get<1>(E[i][j + 1]); k++){ idL[i + 1][k] = P[i + 1] + j; } } } idR[N - 1] = vector<int>(Y[N - 1].size() + 1, P[N]); int V = P[N + 1]; vector<vector<pair<T, int>>> G(V); for (int i = 0; i <= N; i++){ for (int j = P[i]; j < P[i + 1] - 1; j++){ G[j].push_back(make_pair(get<2>(E[i - 1][j - P[i] + 1]), j + 1)); G[j + 1].push_back(make_pair(get<2>(E[i - 1][j - P[i] + 1]), j)); } } for (int i = 0; i < N; i++){ for (int j = 0; j <= Y[i].size(); j++){ G[idL[i][j]].push_back(make_pair(e, idR[i][j])); G[idR[i][j]].push_back(make_pair(e, idL[i][j])); } } return G; } int find(point p){ int x = lower_bound(X.begin(), X.end(), p.x - eps) - X.begin(); if (x == 0){ return 0; } else if (x == N){ return P[N]; } else { int ans = P[x]; for (int i = 1; i < E[x - 1].size() - 1; i++){ point A(X[x - 1], Y[x - 1][get<0>(E[x - 1][i])]); point B(X[x], Y[x][get<1>(E[x - 1][i])]); if (cross(B - A, p - A) > 0){ ans++; } } return ans; } } vector<double> area(){ int V = P[N + 1]; vector<double> ans(V, -1); for (int i = 0; i < N - 1; i++){ for(int j = 1; j < E[i].size() - 2; j++){ double l = Y[i][get<0>(E[i][j + 1])] - Y[i][get<0>(E[i][j])]; double r = Y[i + 1][get<1>(E[i][j + 1])] - Y[i + 1][get<1>(E[i][j])]; ans[P[i + 1] + j] = (l + r) * (X[i + 1] - X[i]) / 2; } } return ans; } vector<double> length(){ int V = P[N + 1]; vector<double> ans(V, -1); for (int i = 0; i < N - 1; i++){ for(int j = 1; j < E[i].size() - 1; j++){ double l = Y[i][get<0>(E[i][j])]; double r = Y[i + 1][get<1>(E[i][j])]; ans[P[i + 1] + j - 1] = hypot(X[i + 1] - X[i], r - l); } } return ans; } }; int main(){ cout << fixed << setprecision(20); while (true){ int N, R; cin >> N >> R; if (N == 0 && R == 0){ break; } vector<point> P(N + 1); for (int i = 0; i < N; i++){ cin >> P[i].x >> P[i].y; } P[N] = P[0]; vector<vector<point>> P2(N, vector<point>(6)); for (int i = 0; i < N; i++){ point A = P[i], B = P[i + 1]; if (A.x + A.y > B.x + B.y){ swap(A, B); } if (A.x - A.y > B.x - B.y){ P2[i][0] = point(A.x - R, A.y); P2[i][1] = point(B.x - R, B.y); P2[i][2] = point(B.x, B.y + R); P2[i][3] = point(B.x + R, B.y); P2[i][4] = point(A.x + R, A.y); P2[i][5] = point(A.x, A.y - R); } else { P2[i][0] = point(A.x - R, A.y); P2[i][1] = point(A.x, A.y + R); P2[i][2] = point(B.x, B.y + R); P2[i][3] = point(B.x + R, B.y); P2[i][4] = point(B.x, B.y - R); P2[i][5] = point(A.x, A.y - R); } } for (int i = 0; i <= N; i++){ P[i] = rotate(P[i], 1); } for (int i = 0; i < N; i++){ for (int j = 0; j < 6; j++){ P2[i][j] = rotate(P2[i][j], 1); } } slab_decomposition<__int128_t> D(bit_xor<__int128_t>(), 0); for (int i = 0; i < N; i++){ D.add_segment(line(P[i], P[i + 1]), 1); } for (int i = 0; i < N; i++){ for (int j = 0; j < 6; j++){ D.add_segment(line(P2[i][j], P2[i][(j + 1) % 6]), (__int128_t) 1 << (i + 1)); } } vector<vector<pair<__int128_t, int>>> E = D.build(); int V = E.size(); vector<__int128_t> A(V, -1); A[0] = 0; queue<int> Q; Q.push(0); while (!Q.empty()){ int v = Q.front(); Q.pop(); for (pair<__int128_t, int> e : E[v]){ int w = e.second; if (A[w] == -1){ A[w] = A[v] ^ e.first; Q.push(w); } } } vector<double> L = D.length(); double ans = 0; for (int i = 0; i < V - 1; i++){ if (A[i] != 1 && A[i + 1] == 1 || A[i] == 1 && A[i + 1] != 1){ ans += L[i]; } } cout << ans << endl; } }