题目大意
思路讲解

原先的凸性检测没法检测出这样子的五角星。
1 | // 不允许自交的多边形,顺序都可以,会把 poly 变为 CCW 顺序(逆时针) |
AC代码
AC
https://qoj.ac/submission/2002045
AC https://qoj.ac/submission/2002100
AC https://qoj.ac/submission/2002121
1 | /** |

原先的凸性检测没法检测出这样子的五角星。
1 | // 不允许自交的多边形,顺序都可以,会把 poly 变为 CCW 顺序(逆时针) |
AC
https://qoj.ac/submission/2002045
AC https://qoj.ac/submission/2002100
AC https://qoj.ac/submission/2002121
1 | /** |
AC
https://qoj.ac/submission/2001485
1 | /** |
在一个基于2D物理的视频游戏中,两个角色P和Q在战场上移动。每个角色都由一个凸多边形表示,用作其碰撞箱。当两个碰撞箱重叠时,游戏会计算它们的交集区域作为碰撞伤害。
然而,由于竞技场中不可预测的风流,在任意平移向量t=(tx,ty)的作用下,角色Q可以被位移,但不能旋转或翻转。您需要计算当Q均匀随机放置以确实与P发生碰撞时的预期碰撞伤害,即它们的交集具有正面积。
更正式地,定义f(t)为表示角色P的多边形和表示角色Q的多边形在沿t平移后的交集区域的面积。设D⊆R2为所有平移向量t的集合,使得f(t)>0。可以证明D具有正面积,记为∣D∣。您需要计算∣D∣1D∬f(t)dt。
根据积分几何中的运动学公式,两个形状在所有可能的平移下的交集面积的积分,等于这两个形状面积的乘积。
∬R2Area(P∩Q(t))dt=Area(P)×Area(Q)
因此,分子只需计算两个多边形面积的乘积。
原因可以用以下离散化像素法解释:
想象一下,我们将 P 和 Q 都放在一个非常细的网格纸上,把它们看作是由无数个微小的像素点组成的。
假设 P 由 N 个像素点组成(代表面积 SP)。
假设 Q 由 M 个像素点组成(代表面积 SQ)。
我们要求的积分 ∬Area(P∩Q(t))dt,本质上是在问:当我们把 Q 移动到所有可能的位置时,总共有多少次“像素重叠”发生?
让我们换个角度思考:
取 Q 中的任意一个特定像素点 q0。
当 Q 在平面上到处移动时,这个点 q0 会经过平面的每一个位置。
它什么时候会和 P 发生“重叠”?也就是当 q0 落在 P 内部的时候。
因为 P 有 N 个像素,所以 q0 会在 N 个不同的位置(平移向量)上为“总重叠面积”贡献 1 个单位。
现在,对 Q 中的每一个像素点都重复这个过程。Q 总共有 M 个像素点。
因此,总的重叠贡献 = (Q 的像素数) × (P 的像素数) = M×N。
这就对应了 Area(Q)×Area(P)。
总的来说,就是以 Q 为主,那么 Q 中每一个点都可以和这个 P 中每一个点相交,P 中点的数量就是这个 Area(P),那么 Q 中一共有 Area(Q) 个点,总的积分就是 Area(P)×Area(Q)。
集合 D 表示所有能让 P 和 Q 接触或重叠的平移向量 t。这个集合实际上就是 P 和 −Q(Q 关于原点的中心对称图形)的闵可夫斯基和:
D=P⊕(−Q)
由于 P 和 Q 都是凸多边形,它们的闵可夫斯基和 D 也是一个凸多边形。
构建 D 的方法:
凸多边形的闵可夫斯基和可以通过将两个多边形的所有边向量收集起来,按照极角排序,然后首尾相连构成一个新的多边形。
P 的边向量:Pi+1−Pi。
−Q 的边向量:−Q 的边相当于 Q 的边的反向。即如果 Q 的边是 Qj+1−Qj,那么对应的 −Q 的边向量就是 Qj−Qj+1。
1 | void Solve() { |
AC
https://codeforces.com/gym/106252/submission/361314647
1 | /** |
"
Sulfox the fennec fox已经建立了一个迷宫,并邀请他的两个朋友Alice和Bob来尝试。迷宫可以抽象为一个简单的、连通的无向图,其中有n个顶点和m条边,Alice和Bob在两个指定的不同顶点开始。
在任何玩家移动之前,Sulfox必须为每条无向边选择一个方向,将迷宫变成一个有向图。然后,为了找到对方,两位玩家在定向迷宫内进行回合移动。在每一轮中,他们同时独立行动,不留下任何标记或沟通;相反,他们采取“最愚蠢”的策略:
如果玩家当前位于一个具有一个或多个出边的顶点,则他们会任意穿越一个出边到相邻顶点;
否则,如果他们当前的顶点没有出边,则他们将停留在该顶点。
作为对自己迷宫设计技能感到自豪的Sulfox,希望将通道定向,以便Alice和Bob永远不会相遇。鉴于无向图和Alice和Bob的起始顶点,请帮助Sulfox定向每条边,使得无论Alice和Bob如何在每一轮中做出选择,他们永远不会在任何一轮结束时占据同一顶点。如果不存在这样的定向,请报告。"
我记得是这样子的,只要两个玩家所在顶点不直接相连,就简单了,所有连着两个点的边全部指向这两点,可以把这两个玩家封死。
两个玩家所在顶点直接相连,就找最小的环,所有其他点(边)指向这个环,避免两个玩家离开这个环。
我们需要两次 bfs 实现:
这一部分 bfs 实现,负责这个找到包含 X→Y 的环(这个相对容易),如果知道到了,直接输出答案即可。
1 | for (int i=1;i<=1;++i) { |
如果没有包含 X→Y 的这个环,就从 X 出发,随便找一个这个无弦环即可。

为什么需要这两个部分呢?

只要我们在第一阶段排除这样子的包含 X→Y 的这个环,我们找到的环上的点的边就不会和 Y 相连。(可以避免出现一些奇奇怪怪的情况)
1 | if (isout==false){ |
AC
https://qoj.ac/submission/2005037
1 | /** |
这道题目的 SPJ 怎么写?
编写这道题 SPJ(Special Judge)的核心难点在于:如何验证“无论怎么走都遇不到”这一条件。
因为题目中描述的移动策略是“任意选择一条出边(arbitrarily traverse)”,这意味着玩家的移动是不确定的。你不能只模拟一条路径,而是必须模拟所有可能的路径。
要在 SPJ 中解决这个问题,关键点不是记录 Alice 和 Bob “在哪一个点”,而是记录他们当前可能在哪些点的集合。
由于 N≤300,我们可以使用 C++ 的 std::bitset<305> 来高效地维护这个集合。
首先,必须确保选手输出的图是合法的:
边数对应:选手输出的有向边必须和输入的无向边一一对应(通过 multiset 或排序后比对)。
点范围:点的编号在 1…N 之间。
这是 SPJ 最关键的部分。
设 SA(t) 为第 t 轮结束时,Alice 可能所在的所有顶点的集合。
设 SB(t) 为第 t 轮结束时,Bob 可能所在的所有顶点的集合。
初始状态:
SA(0)={Alicestart},SB(0)={Bobstart}
每一轮模拟时,我们需要计算下一轮的集合 S(t+1)。规则如下:
对于集合中的每一个点 u∈S(t):
情况 A(有出边):如果 u 有出边指向 v1,v2,…,那么在下一轮,Alice/Bob 可能会出现在这些邻居中的任何一个。将 {v1,v2,…} 全部加入 S(t+1)。
情况 B(无出边):如果 u 没有出边,根据题目规则,玩家停留在原地。将 u 加入 S(t+1)。
用位运算表示非常快:
1 | next_mask = 0; |
在任何时刻 t,如果 Alice 和 Bob 的可能位置集合有交集,说明存在一种走法让他们相遇,判为 WA。
(S_A & S_B).any() == true模拟不能无限进行下去。我们有两个终止条件:
相遇:检测到交集,直接 quitf(_wa, ...)****。
**死循环(安全):**如果当前的“状态对” (SA,SB) 曾经出现过,说明我们进入了一个循环,而且在这个循环中没有发生碰撞。那么继续跑下去也不会碰撞。此时判为 AC。
set<pair<string, string>> history 记录 bitset 的状态。这是一个标准的写法,你可以直接参考:
1 | #include "testlib.h" |
这道题 SPJ 的精髓在于不要去想“他们走了哪条路”,而是利用 N 较小的特点,并行追踪“他们可能在的所有位置”。只要这两个位置集合永远不相交,选手的答案就是正确的。
亚历克斯正在规划在一个简化的台湾高速公路系统模型上设立休息区。该系统包含n个互通立交,由n−1条双向道路连接。网络是连通的,任意一对互通立交之间有且仅有一条最短路径。第i条道路连接互通ui和vi,长度为li。
可以建造恰好k个带加油站的休息区,每个位于一个互通。司机可以从任意互通开始旅行到任何其他互通,始终沿着唯一最短路径。他们每次旅行都会带满油箱,只能在设有休息区的互通处加油。
亚历克斯想知道可能的最小燃油箱容量d,以便可以以一种方式放置这k个休息区,确保没有任何司机会耗尽燃油。在任何旅行中,司机永远不应该在沿途行驶超过d个单位而不经过休息区,包括旅程的开始或结束。目标是找出最小的d,假设休息区被以最佳方式放置。
https://hackmd.io/@tmt514/Bk_lRqjill#Problem-J—Gas-Station
觀察到如果選擇了一個比正確答案還要小的 d′,則需要的加油站數量會大於 k,反之小於等於 k。因此藉由二分搜尋法,透過指定的 d′ 計算所需要的加油站數量,來檢驗選定的 d′ 是太大或是太小。
對於計算加油站數量的部分,由於我們固定了 d′,因此可以簡單的使用 DFS 來計算。設 dfs(r) 為以 r 為根的子樹,r 到最近的加油站距離。對於 r 的子樹 (u,l),若
说白了,只要每一颗子树都最小化加油站数量,然后我们再检测在最小化加油站数量前提下,是否违反了限制。
贪心的关键是,找到最优子结构。最优子结构是什么?就是在不在这贪取决于什么!
1 | auto dfs=[&](auto && self,ll u,ll p) -> ll { |
AC
https://codeforces.com/gym/106084/submission/361152548
1 | /** |