JOISC 2021 部分题目做题笔记
P7560 [JOISC 2021 Day1] フードコート
整个队列不能直接维护,并且队首有一些人离开很麻烦。首先考虑去掉离开操作:假设你知道一次询问时,该店已经离开的人数为 \(l\),则去掉离开操作后,实际询问的人是 \(B+l\)。因此我们需要求出每个询问时,该店已经离开的人数。直接动态维护人数也不方便,考虑有离开人数=总的到来人数-当前人数,我们考虑同时维护两个值。总到来人数可以直接区间加,当前人数需要支持区间加、区间减、区间对 0 取 max,同时单点查询。由于查询是单点的,可以用线段树维护当前区间内的人数最小值。具体地,我们维护两个 tag,分别是区间加/对一个数取 max,假定是先加减,再取 max,对区间取 max 操作可以直接合并;对加减操作,就将两个 tag 同时加上当前要加的值即可。合并 tag 的时候,假设原有 tag 为 \((a,b)\),新 tag 为 \((c,d)\),合并之后就是 \((a+c,\max(b+c,d))\)。这是容易维护的。
此时,问题变为有若干个区间加正数的操作,多个询问,问某个点首次大于等于一个值的时间。考虑扫描线扫序列,维护时间轴。一个区间加操作等价于在 \(l\) 位置,给一个时间的后缀加上了一个数,在 \(r+1\) 的位置减去了一个数。需要维护的操作是区间加、维护区间最大值,二分首个大于某个值的位置。线段树是好做的。最后就是找那一次操作加的是哪个颜色即可,若最终答案晚于询问时间,即为 -1。
P7561 [JOISC 2021 Day2] 道路の建設案
最直接的想法是多路归并。但是无论在曼哈顿意义下还是在切比雪夫意义下,每一路的下一个值不好直接找到。考虑一个不需要严格按顺序找到的方法。考虑二分答案 \(mid\),对距离小于等于 \(mid\) 的点对计数,选取最小的满足数量大于等于 \(k\) 的作为答案。注意到转化为切比雪夫距离后,需要满足的条件实际上是 \(|x_i-x_j|\geq mid\) 且 \(|y_i-y_j|\geq mid\),是一个二维数点形式。但这里不能直接数,因为我们最终求答案时,需要找到具体的距离值。考虑将所有点按照 \(x\) 排序,对 \(x\) 滑动窗口,并钦定一个偏序(避免算重),把窗口内的点的 \(y\) 扔进 set,每次二分到 \(y\) 的下界,然后再往上依次遍历。每次总共最多遍历 \(k\) 个即可判定答案。最终构造解,只需要把 \(mid-1\) 扔进去做上述操作,剩的用 \(mid\) 补全即可。
P7562 [JOISC 2021 Day4] イベント巡り 2 (Event Hopping 2)
字典序最小的经典求解方案是依次枚举每一个,看加入之后是否可能合法。具体地,我们对时间做离散化,考虑用 set 维护空闲的连续段。对于一个待判断是否要加入的 \(i\),首先在 set 中二分并判定其是否完全处于某个空闲的段内;然后是将那个段取出来,分成两个段并测试现在空闲段内能装的数量最大值是否满足条件。注意到我们不好快速查询一个完整状态能装的最大值;但是,由于每次修改的只有一个段,我们只需要同时维护每个段内能装的最大值,在上一次的状态上修改即可。
于是考虑如何快速求某一个段内能装的线段数量。你发现对于当前序号之前的所有线段,若已被选择,则一定不会与当前求的段有交;若未被选择,则说明选了它一定不优,不会对我的贪心过程产生影响,因而我不必考虑线段序号之间的偏序关系,这变成了一个静态的问题。考虑贪心的过程是选择右端点最靠前的一个,这启发我们设 \(dp_{i,j}\) 表示从 \(i\) 开始,选 \(j\) 个的最小右端点。更进一步地,这个东西可以倍增合并,因此总复杂度可以做到 \(O(n\log n)\),其中包括 set 的部分和倍增的部分。不难通过。
P7563 [JOISC 2021 Day4] 最悪の記者 4
\(i\) 向 \(a_i\) 连边形成内向基环树。先考虑树的情况,我们要求父亲的值不大于儿子的值,对权值离散化后设 \(dp_{i,j}\) 表示第 \(i\) 个点权值为 \(j\) 的最小代价,有转移 \(dp_{i,j}=\sum_{v\in son_i} \min_{k=j}^m dp_{v,k}+[h_i\neq j]c_i\)。后面不等的艾弗森括号可以通过提前给答案加上 \(\sum c_i\) 转成单点减。然后取 \(\min\) 的部分就是 tricky 的线段树合并维护整体 dp,先走右子树,再走左子树,节点维护区间 min,边走边维护后缀 min 即可。注意线段树合并维护整体 dp,在一边有一边没有需要返回时必须打 tag。
注意到线段树合并是类似树形背包的逐子树合并,因此我们不提前钦定一个点一定选 \(j\),转而维护目前的子树状态下选 \(j\),则此时同时维护两棵子树的后缀最小值,答案应当为 \(p\) 的当前值加 \(q\) 的后缀最小值,与 \(q\) 的当前值加 \(p\) 的后缀最小值中的较小的一个。全过程中细节较多,比如一个点的值实际上是后缀的最小值,因此做单点查值,或是单点修改的时候,那个点的实际值应当是后缀的最小值,应当做区间查询。
基环树的情况,先拓扑排序缩点变成树,执行上述操作。由于每个根节点的值要么是所有缩起来的点中的某个值,要么是最小值,枚举是哪一个,然后下去查询即可。总复杂度 \(O(n\log n)\)。
P7564 [JOISC 2021 Day3] ボディーガード
考虑以时间和位置建立平面直角坐标系。那么顾客和保镖的运动轨迹均应当是斜率为 \(1\) 或 \(-1\) 的线段,且线段长度为实际距离的 \(\sqrt 2\) 倍。斜向的不好处理,考虑将坐标系旋转 \(45°\),根据几何关系可以得知原先位于 \((x,y)\) 的点现在会变到 \((\frac{x-y}{\sqrt 2},\frac{x+y}{\sqrt 2})\)。考虑再给坐标乘上 \(\sqrt 2\),于是此时新坐标为 \((x-y,x+y)\),线段实际长度为原来长度的恰好 \(2\) 倍。由于价值均为偶数,我们可以直接将价值折半抵消长度的增长。此时的列车变为了平面直角坐标系中 \(n\) 条水平或竖直的线段,保镖从出发点开始走,只能向右或向上走,希望其路径覆盖的线段长度乘上新的价值最大化。
不难发现保镖在坐标系中拐弯的点一定与某个线段的端点处在同一水平坐标或同一竖直坐标上。这样的横纵坐标分别有 \(O(n)\) 个。把它们都拿出来,会形成一个网格图,跑 dp 就可以求出从任意格点出发的最大收益。
考虑初始时不在格点的情况。小烟的走法一定是向右或向上走到某条网格图的格线上,然后再向上或向右走到格点处。考虑分先向上和先向右两种方案分别计算。以先向上走为例,初始点到右侧最近的一条纵向格线 \(j\) 的距离为 \(d\),若在横向格线 \(i\) 处转向,则总的收益为 \(c_i\times d+dp_{i,j}\)。这显然是一个 \(y=kx+b\) 的形式,对每个纵向格线间隔中的点拿出来,和这之中的所有横向格线一起排序,从上往下做扫描线,格线的所有信息插入李超线段树,询问就直接查询即可。注意需要对 \(d\) 做离散化,否则李超树初始化复杂度太 大,会超时。复杂度 \(O((n^2+Q)\log n)\),可以通过。当然你也可以发现,距离右上角的越远的位置,dp 值越大,也就可以做斜率优化,维护一个单调队列,每个询问对其二分即可。复杂度有略微的优化,为 \(O(n^2+Q\log n)\)。
P9734 [JOISC 2021 Day2] 逃走経路
你发现点数和边数完全支持我们对 \(n\) 个起点都跑一次最短路的。问题在于时间太多,没办法对每个时间都跑一次。但是显然地,并不是每个时间都有意义。下面分两条路考虑:
首先是路程在一天内结束的方案。我们找到全过程中,到达 \(v\) 的时间距离终止时间最近的一条边 \((u,v,l,c)\),假设我们于 \(t\) 时刻出发,实际是 \(x\) 时刻到的 \(v\),则出发时刻从 \(t\sim t+c-x\) 都是等效的。于是我们枚举每一条限制边,从 \(u\) 出发维护 \(dis_x\) 表示最短时间,进而可以算出每个点出发,当天到,能通过 \((u,v)\) 的最小出发时间。然后再维护 \(to_x\) 表示当天于 \(c\) 时刻从 \(v\) 出发,到其它点的最小时间,则对于每个满足出发时间限制的询问,都有一组解 \(dis_x+to_y+l\)。对于每个点对 \((u,v)\) 维护求到的所有 \(O(m)\) 种方案,将出发时间限制排序并对总时长做后缀 \(\min\),每个询问二分即可。
对于路程跨天的方案,你发现一旦过了一天,第二天早上的出发时间一定是 \(0\) 时刻,可能情况大大减少。我们直接枚举首个休息点,仍然求出 \(dis_x\)、\(to_y\),对每个询问也直接做枚举+判断即可。
由于可能是完全图,dij 的过程可以直接用 \(O(n^2+m)\) 的算法实现,更优。故总复杂度为 \(O(m(n^2+m)+n^2m\log m+q\log m+qn)\),不难通过。

浙公网安备 33010602011771号