AT_abc251_g Intersection of Polygons Solution
escapist404
2023-06-11 00:49:53
# AT_abc251_g Intersection of Polygons Solution
## Preface
由于某些 $\LaTeX$ 的原因,本文的公式无法正常查看,建议读者访问[博客](https://www.luogu.com.cn/blog/284754/solution-at-abc251-g)以获得正常阅读体验。
## Statement
逆时针地给定一个有 $N$ 个顶点,第 $i$ 个顶点为 $(x_i, y_i)$ 的凸包 $P_0$。
再给出 $M$ 个向量 $(u_i, v_i)$ 代表凸包 $P_1, P_2, \cdots, P_M$,凸包 $P_j$ 有 $N$ 个顶点,第 $i$ 个顶点为 $(x_i + u_j, y_i + v_j)$。
最后有 $Q$ 组询问,每次给定一个点 $(a_i, b_i)$,要求判断这个点是否在每一个凸包的内部。
*注意凸包的边上也算是它的内部。*
保证 $3 \le N \le 50$,$1 \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
$$
可以使用右手定则判断叉积的正负。
可参考下图进行理解:
![](https://cdn.luogu.com.cn/upload/image_hosting/czw4udr6.png)
这样朴素地求解是 $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}
$$
$(S)$ 式等价于
$$
\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;
}
```