0%

洛谷 P5939 - 折线

题目大意

洛谷 P5939 [POI 1998 R3] 折线

平面上给 nn 个格点。一条 平直折线 从左到右一笔画过去,每一段和 xx 轴的夹角都在 [45,45][-45^\circ, 45^\circ] 之间。问:最少画多少条平直折线,才能盖住所有点?

数据范围

1n300001 \le n \le 300000xi,yi300000 \le x_i, y_i \le 30000 。时限按常规 1s 算,目标复杂度 O(nlogn)O(n\log n)

样例

样例 1:5 个点 (2,3) (3,4) (4,5) (1,6) (12,27) ,答案 3 。样例 2:6 个点,答案 3 。

思路讲解

一句话

夹角 45\le 45^\circ 等价于斜率绝对值 1\le 1 ,也就是 ΔyΔx|\Delta y| \le \Delta x 。把坐标系转 4545^\circ ,令 u=x+yu = x+yv=xyv = x-y ,"两点能串在同一条折线上"就变成 (u,v)(u,v) 两维都不减 的支配关系;于是一条折线 = 一条 ,最少折线数 = 最小链覆盖 ;由 Dilworth(导弹拦截那一套),它等于 vv 序列的 最长严格下降子序列 长度, O(nlogn)O(n\log n)

第一步:把角度约束代数化

设两个格点 P=(x1,y1)P=(x_1,y_1)Q=(x2,y2)Q=(x_2,y_2) ,不妨 x1x2x_1 \le x_2 。“能用一段满足角度约束的线段把 QQ 接在 PP 后面”,精确条件就是

y2y1x2x1|y_2 - y_1| \le x_2 - x_1

夹角 45\le 45^\circ 嘛,斜率绝对值就 1\le 1 ,竖直跨度不能超过水平跨度。

边界要钉一刀: x1=x2x_1 = x_2 时退化成 Δy0|\Delta y| \le 0 ,即 P,QP,Q 同点——所以两个不同的同 xx 点永远进不了同一条折线(左到右的折线对 xx 严格单调)。后面实现的并列处理会用到这点。

第二步:45° 旋转,变成二维支配

把绝对值拆成两条同时成立的不等式:

y2y1x2x1y1y2x2x1y_2 - y_1 \le x_2 - x_1 \quad\text{且}\quad y_1 - y_2 \le x_2 - x_1

两条都做纯移项,让 PPQQ 彻底分到不等号两侧,冒出来两个组合量。令

u=x+y,v=xy\textcolor{blue}{\boldsymbol{u = x + y}}, \qquad \textcolor{blue}{\boldsymbol{v = x - y}}

条件就清爽成了 uPuQu_P \le u_QvPvQv_P \le v_Q

这里有个 易错点 :是 不下降( \le ,含等号) ,不是"严格上升"。反例 P=(0,0)P=(0,0)Q=(1,1)Q=(1,1)uu 从 0 升到 2, vv 从 0 到 0 没变 ;但 Δy=0Δx=1|\Delta y| = 0 \le \Delta x = 1QQ 完全能接在 PP 后面。按"严格"就会误判。

几何上:第一步那个从 PP 朝右张开的 9090^\circ 锥形,换到 (u,v)(u,v) 系里锥的两条边正好贴着新坐标轴,能接就变成最干净的一句—— QQ 落在 PP 的右上方(含边界),标准的二维支配。

第三步:一条折线 = 一条链 → 最小链覆盖

把 pair 关系抬到整条折线:一条平直折线经过的那串点,按经过顺序看, (u,v)(u,v) 两维都单调不减——这就是 (u,v)(u,v) 支配偏序里的一条

(u,v)(u,v) 字典序排序( uu 升,同 uuvv 升——这个并列方向正好让同 uu 的点能进同一条链,不会被错算)。排完抽出 vv 序列,一条折线就对应 vv 的一个 非降子序列 。于是整道题变成:用最少个 " vv 非降子序列" 覆盖整条 vv 序列。

第四步:Dilworth / 导弹拦截

“用最少个非降子序列覆盖一个序列”——这正是导弹拦截那道经典题的对偶。由 Dilworth 定理:

关键结论:最少非降子序列覆盖数 = 该序列的最长严格下降子序列长度。

所以答案 = vv 序列的最长严格下降子序列长度。实现上不用真去求"下降":把 vv 取负,求 v-v最长严格上升子序列 (patience sorting / lower_boundO(nlogn)O(n\log n) )即可。

📎 动画与源码

完整推导对话录(含锥形配图、 (u,v)(u,v) 支配区配图、Dilworth 方向对照表)见页面末尾 附件mentor.pdf ;完整 AC 源码见下方 AC 代码 折叠块,也在附件里附了 .cpp.txt 方便复制对拍。

AC 代码

AC 提交(vjudge)

源码较长(自带几何模板 + LIS 类),完整 C++ 折叠在下方;同一份也作 .cpp.txt 附件挂在页面末尾。

心路历程(一次方向写反 → AC)

  • Dilworth 方向写反:第一版写成 LIS(uv, false) ——求的是 vv最长非降子序列 ,量根本不对。它和"最小链覆盖"没关系,只是在样例 1 上巧合都等于 3,没暴露。

  • 照出 bug 的反例:输入 2 / 0 0 / 2 0 。两点 (0,0),(2,0)(0,0),(2,0) 在同一条水平折线上(夹角 00^\circ ),正解 = 1 ;错误代码: (u,v)=(0,0),(2,2)(u,v)=(0,0),(2,2)vv 序列 [0,2][0,2] ,最长非降 = 2 → 输出 2 ,错。

  • 修法:改成求 vv 的最长严格下降子序列——uv[i] = -uvA[i].y; 取负后 LIS(uv, true); (strict 走 lower_bound ),等价于求最长严格下降。一发 AC。

  • 沉淀的易错点:(1) Dilworth 是"最少非降覆盖 = 最长 严格下降 “,方向 / 严格性都不能错;(2) 支配条件是"不下降"含等号,不是"严格上升”,同 uu / 同 vv / 重点都得能进同一条链;(3) 排序并列方向(同 uuvv 升)要和"求严格下降"配套,弄反会把可同链的点错算成反链。

附件

下面附件均已直接传入本页面,可在 Notion 内直接预览:

  • mentor.pdf —— 完整带教对话录(PDF 版),含 5 步推导 + 锥形 / 支配区 TikZ 配图 + Dilworth 方向对照表 + 反例。

  • P5939_POI_1998_R3_.cpp.txt —— 完整 AC 源码(下载后改回 .cpp ,含几何模板 + LIS 类,方便复制对拍)。

完整带教对话录(PDF): 5 步推导 + 锥形/(u,v)支配区 TikZ 配图 + Dilworth 方向对照表 + 反例

P5939_POI_1998_R3_.cpp.txt