0%

2025-沈阳站-区域赛-G. Collision Damage(正式开始攻克计算几何)(两个形状在所有可能的平移下的交集面积的积分,等于两个面积的和)(minkowski 即合成大凸包)

题目大意

在一个基于2D物理的视频游戏中,两个角色PPQQ在战场上移动。每个角色都由一个凸多边形表示,用作其碰撞箱。当两个碰撞箱重叠时,游戏会计算它们的交集区域作为碰撞伤害。

然而,由于竞技场中不可预测的风流,在任意平移向量t=(tx,ty)\mathbf{t} = (t_x, t_y)的作用下,角色QQ可以被位移,但不能旋转或翻转。您需要计算当QQ均匀随机放置以确实与PP发生碰撞时的预期碰撞伤害,即它们的交集具有正面积。

更正式地,定义f(t)f(\mathbf{t})为表示角色PP的多边形和表示角色QQ的多边形在沿t\mathbf{t}平移后的交集区域的面积。DR2D \subseteq \mathbb{R}^2为所有平移向量t\mathbf{t}的集合,使得f(t)>0f(\mathbf{t}) > 0。可以证明DD具有正面积,记为D|D|。您需要计算1DDf(t)dt\frac{1} {|D|} \iint\limits_{D} f(\mathbf{t}) \, d\mathbf{t}

思路讲解

1. 分子分析 (Df(t)dt\iint_D f(\mathbf{t}) \, d\mathbf{t})

根据积分几何中的运动学公式,两个形状在所有可能的平移下的交集面积的积分,等于这两个形状面积的乘积。

R2Area(PQ(t))dt=Area(P)×Area(Q)\iint_{\mathbb{R}^2} \text{Area}(P \cap Q(\mathbf{t})) \, d\mathbf{t} = \text{Area}(P) \times \text{Area}(Q)

因此,分子只需计算两个多边形面积的乘积。

原因可以用以下离散化像素法解释:

想象一下,我们将 PPQQ 都放在一个非常细的网格纸上,把它们看作是由无数个微小的像素点组成的。

  • 假设 PPNN 个像素点组成(代表面积 SPS_P)。

  • 假设 QQMM 个像素点组成(代表面积 SQS_Q)。

我们要求的积分 Area(PQ(t))dt\iint \text{Area}(P \cap Q(\mathbf{t})) \, d\mathbf{t},本质上是在问:当我们把 QQ 移动到所有可能的位置时,总共有多少次“像素重叠”发生?

让我们换个角度思考:

  1. QQ 中的任意一个特定像素点 q0q_0

  2. QQ 在平面上到处移动时,这个点 q0q_0 会经过平面的每一个位置。

  3. 它什么时候会和 PP 发生“重叠”?也就是当 q0q_0 落在 PP 内部的时候。

  4. 因为 PPNN 个像素,所以 q0q_0 会在 NN 个不同的位置(平移向量)上为“总重叠面积”贡献 1 个单位。

  5. 现在,对 QQ 中的每一个像素点都重复这个过程。QQ 总共有 MM 个像素点。

  6. 因此,总的重叠贡献 = (QQ 的像素数) ×\times (PP 的像素数) = M×NM \times N

这就对应了 Area(Q)×Area(P)\text{Area}(Q) \times \text{Area}(P)

总的来说,就是以 QQ 为主,那么 QQ 中每一个点都可以和这个 PP 中每一个点相交,PP 中点的数量就是这个 Area(P)\text{Area}(P),那么 QQ 中一共有 Area(Q)\text{Area}(Q) 个点,总的积分就是 Area(P)×Area(Q)\text{Area}(P)\times\text{Area}(Q)

2. 分母分析 (D|D|)

集合 DD 表示所有能让 PPQQ 接触或重叠的平移向量 t\mathbf{t}。这个集合实际上就是 PPQ-QQQ 关于原点的中心对称图形)的闵可夫斯基和

D=P(Q)D = P \oplus (-Q)

由于 PPQQ 都是凸多边形,它们的闵可夫斯基和 DD 也是一个凸多边形。

构建 DD 的方法

凸多边形的闵可夫斯基和可以通过将两个多边形的所有边向量收集起来,按照极角排序,然后首尾相连构成一个新的多边形。

  • PP 的边向量:Pi+1PiP_{i+1} - P_i

  • Q-Q 的边向量:Q-Q 的边相当于 QQ 的边的反向。即如果 QQ 的边是 Qj+1QjQ_{j+1} - Q_j,那么对应的 Q-Q 的边向量就是 QjQj+1Q_j - Q_{j+1}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Solve() {
ll N, M;
cin >> N >> M;
vector<Point> poly1(N), poly2(M);
for (int i = 0; i < N; ++i) {
cin >> poly1[i];
}
for (int i = 0; i < M; ++i) {
cin >> poly2[i];
poly2[i]=-poly2[i];
}
auto poly_res = minkowski(poly1, poly2);
DB ans = polygonArea(poly1) * polygonArea(poly2) / polygonArea(poly_res);
cout << fsp(12) << ans << "\n";
}

AC代码

AC
https://codeforces.com/gym/106252/submission/361314647

心路历程(WA,TLE,MLE……)