AT_abc251_g Intersection of Polygons Solution

· · 题解

AT_abc251_g Intersection of Polygons Solution

Preface

由于某些 \LaTeX 的原因,本文的公式无法正常查看,建议读者访问博客以获得正常阅读体验。

Statement

逆时针地给定一个有 N 个顶点,第 i 个顶点为 (x_i, y_i) 的凸包 P_0

再给出 M 个向量 (u_i, v_i) 代表凸包 P_1, P_2, \cdots, P_M,凸包 P_jN 个顶点,第 i 个顶点为 (x_i + u_j, y_i + v_j)

最后有 Q 组询问,每次给定一个点 (a_i, b_i),要求判断这个点是否在每一个凸包的内部。

注意凸包的边上也算是它的内部。

保证 3 \le N \le 501 \le M, Q \le 2 \cdot {10}^5-{10}^8 \le x_i, y_i, u_i, v_i, a_i, b_i \le {10}^8

Solution

考虑某个询问 (a, b)

注意到 (a, b) 在多边形 P_j 中,当且仅当其被 N 条边“围住”,考虑利用向量的叉积进行判断。具体地:

对于从 (x_i, y_i)(x_{i + 1}, y_{i + 1}) 的向量

\mathbf{f_i}= \begin{pmatrix} x_{i + 1} + u_j - (x_i + u_j) \\ y_{i + 1} + v_j - (y_i + v_j) \end{pmatrix}= \begin{pmatrix} x_{i + 1} - x_i \\ y_{i + 1} - y_i \end{pmatrix}

考虑另一条从 (x_i + u_j, y_i + v_j)(a, b) 的向量

\mathbf{d_i} = \begin{pmatrix} a - (x_i + v_j) \\ b - (y_i + u_j) \end{pmatrix}

那么,如果有

\mathbf{f_i} \times \mathbf{d_i} \ge 0

\begin{pmatrix} x_{i + 1} - x_i \\ y_{i + 1} - y_i \end{pmatrix} \times \begin{pmatrix} a - (x_i + v_j) \\ b - (y_i + u_j) \end{pmatrix} \ge 0

记为 (S) 式。

就可以说明点 (a, b)\mathbf{f_i} 方向的左侧,而顶点是逆时针给出的,因此 (a, b) 必定在凸包内部。

向量的叉积 规定

\begin{pmatrix} a \\ b \end{pmatrix} \times \begin{pmatrix} c \\ d \end{pmatrix} = ad - bc

可以使用右手定则判断叉积的正负。

可参考下图进行理解:

这样朴素地求解是 O\big(Q N M\big) 的。考虑进行优化。

\begin{pmatrix} a - (x_i + v_j) \\ b - (y_i + u_j) \end{pmatrix}= \begin{pmatrix} a \\ b \end{pmatrix}- \begin{pmatrix} x_i + v_j \\ y_i + u_j \end{pmatrix}

\mathbf{g'_{i, j}} = \begin{pmatrix} x_i + v_j \\ y_i + u_j \end{pmatrix} $$ \begin{pmatrix} x_{i + 1} - x_i \\ y_{i + 1} - y_i \end{pmatrix} \times \begin{pmatrix} a \\ b \end{pmatrix} \ge \begin{pmatrix} x_{i + 1} - x_i \\ y_{i + 1} - y_i \end{pmatrix} \times \begin{pmatrix} x_i + v_j \\ y_i + u_j \end{pmatrix} $$ 即 $$ \mathbf{f_i} \times \begin{pmatrix} a \\ b \end{pmatrix} \ge \mathbf{f_i} \times \mathbf{g'_{i, j}} $$ 该式对所有 $j$ 成立的充要条件是 $$ \mathbf{f_i} \times \begin{pmatrix} a \\ b \end{pmatrix} \ge \max^M_{\mathbf{j}=1} \big\lbrace \mathbf{f_i} \times \mathbf{g'_{i, j}} \big\rbrace = \mathbf{g_i} $$ 这样,我们预处理出 $\mathbf{f_i}$ 和 $\mathbf{g_i}$,在处理单个询问时枚举 $\mathbf i$ 即可。 时间复杂度为 $N + N M + N Q = O\big(N (M + Q)\big)$。 注意 $(x_N, y_N)$ 到 $(x_1, y_1)$ 的向量应当考虑为 $\mathbf{f_N}$。 ## Reference * [判断一个点在直线的哪边 - OI-wiki](https://oi-wiki.org//geometry/2d/#%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E7%82%B9%E5%9C%A8%E7%9B%B4%E7%BA%BF%E7%9A%84%E5%93%AA%E8%BE%B9) * [向量的叉积(叉乘、外积)- OI-wiki](https://oi-wiki.org//math/linear-algebra/product/#%E5%A4%96%E7%A7%AF) * [Accepted Submission](https://atcoder.jp/contests/abc251/submissions/42103528) ## Code ```cpp #include <bits/stdc++.h> using namespace std; typedef long long LL; class Vector { public: LL x, y; Vector(LL _x, LL _y) { x = _x, y = _y; } }; Vector operator + (const Vector x, const Vector y) { return Vector(x.x + y.x, x.y + y.y); } Vector operator - (const Vector x, const Vector y) { return Vector(x.x - y.x, x.y - y.y); } LL cross(const Vector x, const Vector y) { return x.x * y.y - x.y * y.x; } vector <Vector> p, f; vector <LL> g; int n, m, q; int main() { ios::sync_with_stdio(0); cin >> n; for(int i = 1; i <= n; ++i) { LL _x, _y; cin >> _x >> _y; Vector tmp(_x, _y); p.push_back(tmp); } p.push_back(p.front()); for(int i = 0; i < n; ++i) { f.push_back(p[i + 1] - p[i]); } for(int i = 0; i < n; ++i) { g.push_back(-1e18); } cin >> m; for(int i = 1; i <= m; ++i) { LL _u, _v; cin >> _u >> _v; Vector c(_u, _v); for(int j = 0; j < n; ++j) { if(cross(f[j], (p[j] + c)) > g[j]) { g[j] = cross(f[j], (p[j] + c)); } } } cin >> q; for(int i = 1; i <= q; ++i) { LL _a, _b; cin >> _a >> _b; Vector c(_a, _b); bool ans = 1; for(int j = 0; j < n && ans; ++j) { ans &= (cross(f[j], c) >= g[j]); } cout << (ans ? "Yes" : "No") << endl; } return 0; } ```