题解:P11370 [Ynoi2024] 堕天作战/虚空处刑

· · 题解

也许更烂的阅读体验:my blog

不是我想写的,是 lxl 老师布置的题单里有。

仿照 P3316 的思路,我们将多边形的每一条边按横坐标分拆到 O(\log n) 个线段树节点上。

不过现在我们就需要讨论两种情况,一种是查询所在结点在修改(指初始的多边形的边)下方,

我们在所有查询结点在线段树上所有的祖先结点进行操作。

这样修改形成若干贯穿区间的直线,查询形成线段。

根据修改直线形成一个多边形,我们可以得知这些直线是不交的(至多交于端点)。

查询的时候便可以直接二分得到线段两个端点的 rank,比较是否相等就好了。(判掉端点与直线相交)

另一种则是在下方。

这样的话多边形的边形成若干“牙齿”与“耳朵”:

(很像吧)

对于那些牙齿(也就是与区间左右端点相交的连通块),一条线段显然至多只会与它的前驱后继相交。

所以我们对每个牙齿求一个凸包,查询的线段(实质上现在是直线)在凸包上二分就好了。

重点是耳朵。

首先特判这种情况:

这个东西按耳朵与左半区间(右耳朵同理)的交排序之后二分就好了。

然后有一个误区是直线也至多与前驱后继的耳朵相交,但这是不对的。

按左上作为例子,我们需要加入直线左上方的所有耳朵,并将它们的端点求个凸包就好了。这个过程可以离线之后扫描线解决。

合并凸包的时候比较麻烦,但其实我们只用维护凸壳,这个过程用双指针做,细细讨论就好了。

需要对四个方向分别做一遍。

最后有一个小问题,就是查询与修改都垂直于 x 轴,这个特殊处理一下,做一个区间加区间查,不是大问题。

没错这道题就是这样,思维难度不算高,但是代码确实难(也许想清楚了可以很快写完?)。

仿照 UOB 放代码:

代码

有若干样例在我的博客里,当然自己拍才是最好的。

我声称我找到了一种很好的卡常方式(当然可能没用),如果你真的被卡常了可以私信我。